Merge tag 'acpi-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Jun 2020 20:25:52 +0000 (13:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Jun 2020 20:25:52 +0000 (13:25 -0700)
Pull ACPI updates from Rafael Wysocki:
 "These update the ACPICA code in the kernel to upstream revision
  20200430, fix several reference counting errors related to ACPI
  tables, add _Exx / _Lxx support to the GED driver, add a new
  acpi_evaluate_reg() helper, add new DPTF battery participant driver
  and extend the DPFT power participant driver, improve the handling of
  memory failures in the APEI code, add a blacklist entry to the
  backlight driver, update the PMIC driver and the processor idle
  driver, fix two kobject reference count leaks, and make a few janitory
  changes.

  Specifics:

   - Update the ACPICA code in the kernel to upstream revision 20200430:

      - Move acpi_gbl_next_cmd_num definition (Erik Kaneda).

      - Ignore AE_ALREADY_EXISTS status in the disassembler when parsing
        create operators (Erik Kaneda).

      - Add status checks to the dispatcher (Erik Kaneda).

      - Fix required parameters for _NIG and _NIH (Erik Kaneda).

      - Make acpi_protocol_lengths static (Yue Haibing).

   - Fix ACPI table reference counting errors in several places, mostly
     in error code paths (Hanjun Guo).

   - Extend the Generic Event Device (GED) driver to support _Exx and
     _Lxx handler methods (Ard Biesheuvel).

   - Add new acpi_evaluate_reg() helper and modify the ACPI PCI hotplug
     code to use it (Hans de Goede).

   - Add new DPTF battery participant driver and make the DPFT power
     participant driver create more sysfs device attributes (Srinivas
     Pandruvada).

   - Improve the handling of memory failures in APEI (James Morse).

   - Add new blacklist entry for Acer TravelMate 5735Z to the backlight
     driver (Paul Menzel).

   - Add i2c address for thermal control to the PMIC driver (Mauro
     Carvalho Chehab).

   - Allow the ACPI processor idle driver to work on platforms with only
     one ACPI C-state present (Zhang Rui).

   - Fix kobject reference count leaks in error code paths in two places
     (Qiushi Wu).

   - Delete unused proc filename macros and make some symbols static
     (Pascal Terjan, Zheng Zengkai, Zou Wei)"

* tag 'acpi-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (32 commits)
  ACPI: CPPC: Fix reference count leak in acpi_cppc_processor_probe()
  ACPI: sysfs: Fix reference count leak in acpi_sysfs_add_hotplug_profile()
  ACPI: GED: use correct trigger type field in _Exx / _Lxx handling
  ACPI: DPTF: Add battery participant driver
  ACPI: DPTF: Additional sysfs attributes for power participant driver
  ACPI: video: Use native backlight on Acer TravelMate 5735Z
  arm64: acpi: Make apei_claim_sea() synchronise with APEI's irq work
  ACPI: APEI: Kick the memory_failure() queue for synchronous errors
  mm/memory-failure: Add memory_failure_queue_kick()
  ACPI / PMIC: Add i2c address for thermal control
  ACPI: GED: add support for _Exx / _Lxx handler methods
  ACPI: Delete unused proc filename macros
  ACPI: hotplug: PCI: Use the new acpi_evaluate_reg() helper
  ACPI: utils: Add acpi_evaluate_reg() helper
  ACPI: debug: Make two functions static
  ACPI: sleep: Put the FACS table after using it
  ACPI: scan: Put SPCR and STAO table after using it
  ACPI: EC: Put the ACPI table after using it
  ACPI: APEI: Put the HEST table for error path
  ACPI: APEI: Put the error record serialization table for error path
  ...

1907 files changed:
.mailmap
CREDITS
Documentation/ABI/obsolete/sysfs-cpuidle [new file with mode: 0644]
Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt [new file with mode: 0644]
Documentation/ABI/stable/sysfs-devices-node
Documentation/ABI/testing/debugfs-hisi-hpre
Documentation/ABI/testing/debugfs-hisi-sec
Documentation/ABI/testing/debugfs-hisi-zip
Documentation/ABI/testing/dev-kmsg
Documentation/ABI/testing/procfs-smaps_rollup
Documentation/ABI/testing/sysfs-devices-system-cpu
Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update [new file with mode: 0644]
Documentation/Makefile
Documentation/PCI/boot-interrupts.rst
Documentation/RCU/Design/Requirements/Requirements.rst
Documentation/admin-guide/acpi/ssdt-overlays.rst
Documentation/admin-guide/bug-hunting.rst
Documentation/admin-guide/cgroup-v2.rst
Documentation/admin-guide/cpu-load.rst
Documentation/admin-guide/hw-vuln/l1tf.rst
Documentation/admin-guide/init.rst
Documentation/admin-guide/kdump/vmcoreinfo.rst
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/kernel-per-CPU-kthreads.rst
Documentation/admin-guide/mm/userfaultfd.rst
Documentation/admin-guide/nfs/nfsroot.rst
Documentation/admin-guide/numastat.rst
Documentation/admin-guide/perf-security.rst
Documentation/admin-guide/pm/cpuidle.rst
Documentation/admin-guide/pm/intel-speed-select.rst [new file with mode: 0644]
Documentation/admin-guide/pm/intel_pstate.rst
Documentation/admin-guide/pm/working-state.rst
Documentation/admin-guide/pstore-blk.rst [new file with mode: 0644]
Documentation/admin-guide/ramoops.rst
Documentation/admin-guide/ras.rst
Documentation/admin-guide/sysctl/kernel.rst
Documentation/arm64/amu.rst
Documentation/arm64/booting.rst
Documentation/arm64/cpu-feature-registers.rst
Documentation/arm64/elf_hwcaps.rst
Documentation/arm64/silicon-errata.rst
Documentation/conf.py
Documentation/core-api/cachetlb.rst
Documentation/core-api/debugging-via-ohci1394.rst [moved from Documentation/debugging-via-ohci1394.txt with 100% similarity]
Documentation/core-api/dma-api-howto.rst [moved from Documentation/DMA-API-HOWTO.txt with 100% similarity]
Documentation/core-api/dma-api.rst [moved from Documentation/DMA-API.txt with 100% similarity]
Documentation/core-api/dma-attributes.rst [moved from Documentation/DMA-attributes.txt with 100% similarity]
Documentation/core-api/dma-isa-lpc.rst [moved from Documentation/DMA-ISA-LPC.txt with 100% similarity]
Documentation/core-api/index.rst
Documentation/core-api/irq/concepts.rst [moved from Documentation/IRQ.txt with 100% similarity]
Documentation/core-api/irq/index.rst [new file with mode: 0644]
Documentation/core-api/irq/irq-affinity.rst [moved from Documentation/IRQ-affinity.txt with 100% similarity]
Documentation/core-api/irq/irq-domain.rst [moved from Documentation/IRQ-domain.txt with 99% similarity]
Documentation/core-api/irq/irqflags-tracing.rst [moved from Documentation/irqflags-tracing.txt with 100% similarity]
Documentation/core-api/kobject.rst
Documentation/core-api/kref.rst [moved from Documentation/kref.txt with 100% similarity]
Documentation/core-api/printk-basics.rst [new file with mode: 0644]
Documentation/core-api/printk-formats.rst
Documentation/core-api/protection-keys.rst
Documentation/core-api/rbtree.rst [moved from Documentation/rbtree.txt with 100% similarity]
Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/max8998.txt
Documentation/devicetree/bindings/mmc/amlogic,meson-mx-sdhc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
Documentation/devicetree/bindings/mmc/renesas,sdhi.txt
Documentation/devicetree/bindings/mmc/sdhci-msm.txt
Documentation/devicetree/bindings/mmc/sdhci-pxa.txt [deleted file]
Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/anatop-regulator.txt [deleted file]
Documentation/devicetree/bindings/regulator/anatop-regulator.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/maxim,max77826.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/reserved-memory/ramoops.txt
Documentation/devicetree/bindings/rng/arm-cctrng.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt
Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/renesas,rspi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt [deleted file]
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi-dw.txt [deleted file]
Documentation/devicetree/bindings/spi/spi-rspi.txt [deleted file]
Documentation/devicetree/bindings/spi/spi-uniphier.txt [deleted file]
Documentation/devicetree/bindings/spi/ti_qspi.txt
Documentation/devicetree/bindings/vendor-prefixes.yaml
Documentation/doc-guide/maintainer-profile.rst
Documentation/driver-api/dma-buf.rst
Documentation/driver-api/driver-model/device.rst
Documentation/driver-api/driver-model/overview.rst
Documentation/driver-api/index.rst
Documentation/driver-api/ipmi.rst [moved from Documentation/IPMI.txt with 100% similarity]
Documentation/driver-api/nvdimm/nvdimm.rst
Documentation/driver-api/pm/cpuidle.rst
Documentation/driver-api/pm/devices.rst
Documentation/driver-api/thermal/cpu-idle-cooling.rst
Documentation/driver-api/thermal/index.rst
Documentation/fb/efifb.rst
Documentation/features/core/eBPF-JIT/arch-support.txt
Documentation/features/debug/KASAN/arch-support.txt
Documentation/features/debug/gcov-profile-all/arch-support.txt
Documentation/features/debug/kprobes-on-ftrace/arch-support.txt
Documentation/features/debug/kprobes/arch-support.txt
Documentation/features/debug/kretprobes/arch-support.txt
Documentation/features/debug/stackprotector/arch-support.txt
Documentation/features/debug/uprobes/arch-support.txt
Documentation/features/io/dma-contiguous/arch-support.txt
Documentation/features/locking/lockdep/arch-support.txt
Documentation/features/perf/kprobes-event/arch-support.txt
Documentation/features/perf/perf-regs/arch-support.txt
Documentation/features/perf/perf-stackdump/arch-support.txt
Documentation/features/seccomp/seccomp-filter/arch-support.txt
Documentation/features/vm/huge-vmap/arch-support.txt
Documentation/features/vm/pte_special/arch-support.txt
Documentation/filesystems/9p.rst
Documentation/filesystems/automount-support.rst [moved from Documentation/filesystems/automount-support.txt with 92% similarity]
Documentation/filesystems/caching/backend-api.rst [moved from Documentation/filesystems/caching/backend-api.txt with 87% similarity]
Documentation/filesystems/caching/cachefiles.rst [moved from Documentation/filesystems/caching/cachefiles.txt with 90% similarity]
Documentation/filesystems/caching/fscache.rst [new file with mode: 0644]
Documentation/filesystems/caching/fscache.txt [deleted file]
Documentation/filesystems/caching/index.rst [new file with mode: 0644]
Documentation/filesystems/caching/netfs-api.rst [moved from Documentation/filesystems/caching/netfs-api.txt with 91% similarity]
Documentation/filesystems/caching/object.rst [moved from Documentation/filesystems/caching/object.txt with 95% similarity]
Documentation/filesystems/caching/operations.rst [moved from Documentation/filesystems/caching/operations.txt with 90% similarity]
Documentation/filesystems/cifs/cifsroot.rst [moved from Documentation/filesystems/cifs/cifsroot.txt with 72% similarity]
Documentation/filesystems/coda.rst [new file with mode: 0644]
Documentation/filesystems/coda.txt [deleted file]
Documentation/filesystems/configfs.rst [moved from Documentation/filesystems/configfs/configfs.txt with 87% similarity]
Documentation/filesystems/dax.txt
Documentation/filesystems/debugfs.rst
Documentation/filesystems/devpts.rst [new file with mode: 0644]
Documentation/filesystems/devpts.txt [deleted file]
Documentation/filesystems/dnotify.rst [moved from Documentation/filesystems/dnotify.txt with 88% similarity]
Documentation/filesystems/efivarfs.rst
Documentation/filesystems/f2fs.rst
Documentation/filesystems/fiemap.rst [moved from Documentation/filesystems/fiemap.txt with 70% similarity]
Documentation/filesystems/files.rst [moved from Documentation/filesystems/files.txt with 95% similarity]
Documentation/filesystems/fscrypt.rst
Documentation/filesystems/fuse-io.rst [moved from Documentation/filesystems/fuse-io.txt with 95% similarity]
Documentation/filesystems/index.rst
Documentation/filesystems/locking.rst
Documentation/filesystems/locks.rst [moved from Documentation/filesystems/locks.txt with 91% similarity]
Documentation/filesystems/mandatory-locking.rst [moved from Documentation/filesystems/mandatory-locking.txt with 91% similarity]
Documentation/filesystems/mount_api.rst [moved from Documentation/filesystems/mount_api.txt with 79% similarity]
Documentation/filesystems/orangefs.rst
Documentation/filesystems/proc.rst
Documentation/filesystems/quota.rst [moved from Documentation/filesystems/quota.txt with 81% similarity]
Documentation/filesystems/ramfs-rootfs-initramfs.rst
Documentation/filesystems/seq_file.rst [moved from Documentation/filesystems/seq_file.txt with 92% similarity]
Documentation/filesystems/sharedsubtree.rst [moved from Documentation/filesystems/sharedsubtree.txt with 72% similarity]
Documentation/filesystems/spufs/index.rst [new file with mode: 0644]
Documentation/filesystems/spufs/spu_create.rst [new file with mode: 0644]
Documentation/filesystems/spufs/spu_run.rst [new file with mode: 0644]
Documentation/filesystems/spufs/spufs.rst [moved from Documentation/filesystems/spufs.txt with 57% similarity]
Documentation/filesystems/sysfs-pci.rst [moved from Documentation/filesystems/sysfs-pci.txt with 92% similarity]
Documentation/filesystems/sysfs-tagging.rst [moved from Documentation/filesystems/sysfs-tagging.txt with 72% similarity]
Documentation/filesystems/sysfs.rst
Documentation/filesystems/vfs.rst
Documentation/filesystems/xfs-delayed-logging-design.rst [moved from Documentation/filesystems/xfs-delayed-logging-design.txt with 97% similarity]
Documentation/filesystems/xfs-self-describing-metadata.rst [moved from Documentation/filesystems/xfs-self-describing-metadata.txt with 83% similarity]
Documentation/hwmon/amd_energy.rst [new file with mode: 0644]
Documentation/hwmon/bt1-pvt.rst [new file with mode: 0644]
Documentation/hwmon/gsc-hwmon.rst [new file with mode: 0644]
Documentation/hwmon/ina2xx.rst
Documentation/hwmon/index.rst
Documentation/hwmon/lm90.rst
Documentation/hwmon/max16601.rst [new file with mode: 0644]
Documentation/i2c/i2c_bus.svg [moved from Documentation/i2c/i2c.svg with 99% similarity]
Documentation/i2c/summary.rst
Documentation/ia64/irq-redir.rst
Documentation/iio/iio_configfs.rst
Documentation/locking/futex-requeue-pi.rst [moved from Documentation/futex-requeue-pi.txt with 100% similarity]
Documentation/locking/hwspinlock.rst [moved from Documentation/hwspinlock.txt with 100% similarity]
Documentation/locking/index.rst
Documentation/locking/locktorture.rst
Documentation/locking/locktypes.rst
Documentation/locking/percpu-rw-semaphore.rst [moved from Documentation/percpu-rw-semaphore.txt with 100% similarity]
Documentation/locking/pi-futex.rst [moved from Documentation/pi-futex.txt with 100% similarity]
Documentation/locking/preempt-locking.rst [moved from Documentation/preempt-locking.txt with 100% similarity]
Documentation/locking/robust-futex-ABI.rst [moved from Documentation/robust-futex-ABI.txt with 100% similarity]
Documentation/locking/robust-futexes.rst [moved from Documentation/robust-futexes.txt with 100% similarity]
Documentation/locking/rt-mutex.rst
Documentation/maintainer/maintainer-entry-profile.rst
Documentation/memory-barriers.txt
Documentation/misc-devices/index.rst
Documentation/networking/scaling.rst
Documentation/nvdimm/maintainer-entry-profile.rst
Documentation/power/pci.rst
Documentation/power/suspend-and-cpuhotplug.rst
Documentation/powerpc/cxl.rst
Documentation/powerpc/firmware-assisted-dump.rst
Documentation/process/adding-syscalls.rst
Documentation/process/coding-style.rst
Documentation/process/index.rst
Documentation/process/submit-checklist.rst
Documentation/process/unaligned-memory-access.rst [moved from Documentation/unaligned-memory-access.txt with 100% similarity]
Documentation/s390/vfio-ap.rst
Documentation/scheduler/sched-domains.rst
Documentation/security/digsig.rst [moved from Documentation/digsig.txt with 100% similarity]
Documentation/security/index.rst
Documentation/security/lsm.rst
Documentation/security/siphash.rst
Documentation/sphinx/requirements.txt
Documentation/trace/coresight/coresight-ect.rst
Documentation/trace/events.rst
Documentation/trace/ftrace-design.rst
Documentation/translations/it_IT/doc-guide/kernel-doc.rst
Documentation/translations/it_IT/kernel-hacking/hacking.rst
Documentation/translations/it_IT/kernel-hacking/locking.rst
Documentation/translations/it_IT/process/2.Process.rst
Documentation/translations/it_IT/process/adding-syscalls.rst
Documentation/translations/it_IT/process/coding-style.rst
Documentation/translations/it_IT/process/deprecated.rst
Documentation/translations/it_IT/process/email-clients.rst
Documentation/translations/it_IT/process/index.rst
Documentation/translations/it_IT/process/management-style.rst
Documentation/translations/it_IT/process/submit-checklist.rst
Documentation/translations/it_IT/riscv/patch-acceptance.rst [new file with mode: 0644]
Documentation/translations/ko_KR/memory-barriers.txt
Documentation/translations/zh_CN/IRQ.txt
Documentation/translations/zh_CN/filesystems/debugfs.rst [new file with mode: 0644]
Documentation/translations/zh_CN/filesystems/index.rst
Documentation/translations/zh_CN/filesystems/sysfs.txt
Documentation/translations/zh_CN/process/submit-checklist.rst
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt
Documentation/usb/gadget_configfs.rst
Documentation/userspace-api/ioctl/ioctl-number.rst
Documentation/virt/kvm/amd-memory-encryption.rst
Documentation/virt/kvm/api.rst
Documentation/virt/kvm/arm/pvtime.rst
Documentation/virt/kvm/devices/vcpu.rst
Documentation/virt/kvm/hypercalls.rst
Documentation/virt/kvm/mmu.rst
Documentation/virt/kvm/review-checklist.rst
Documentation/vm/index.rst
Documentation/vm/page_frags.rst
Documentation/vm/slub.rst
Documentation/vm/zswap.rst
Documentation/watchdog/convert_drivers_to_kernel_api.rst
Documentation/watchdog/watchdog-kernel-api.rst
Documentation/x86/x86_64/uefi.rst
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/include/asm/checksum.h
arch/alpha/kernel/syscalls/syscall.tbl
arch/alpha/lib/csum_partial_copy.c
arch/arm/Kconfig
arch/arm/boot/compressed/.gitignore
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/atags_to_fdt.c
arch/arm/boot/compressed/efi-header.S
arch/arm/boot/compressed/fdt.c [new file with mode: 0644]
arch/arm/boot/compressed/fdt_ro.c [new file with mode: 0644]
arch/arm/boot/compressed/fdt_rw.c [new file with mode: 0644]
arch/arm/boot/compressed/fdt_wip.c [new file with mode: 0644]
arch/arm/boot/compressed/head.S
arch/arm/boot/compressed/libfdt_env.h [deleted file]
arch/arm/boot/compressed/vmlinux.lds.S
arch/arm/boot/dts/am437x-gp-evm.dts
arch/arm/boot/dts/am437x-idk-evm.dts
arch/arm/boot/dts/am437x-sk-evm.dts
arch/arm/boot/dts/am571x-idk.dts
arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
arch/arm/boot/dts/am57xx-idk-common.dtsi
arch/arm/boot/dts/bcm-hr2.dtsi
arch/arm/boot/dts/bcm2835-rpi-zero-w.dts
arch/arm/boot/dts/dm814x.dtsi
arch/arm/boot/dts/imx6q-b450v3.dts
arch/arm/boot/dts/imx6q-b650v3.dts
arch/arm/boot/dts/imx6q-b850v3.dts
arch/arm/boot/dts/imx6q-bx50v3.dtsi
arch/arm/boot/dts/mmp3-dell-ariel.dts
arch/arm/boot/dts/mmp3.dtsi
arch/arm/configs/omap2plus_defconfig
arch/arm/crypto/sha1_glue.c
arch/arm/crypto/sha1_neon_glue.c
arch/arm/crypto/sha256_glue.c
arch/arm/crypto/sha256_neon_glue.c
arch/arm/include/asm/assembler.h
arch/arm/include/asm/checksum.h
arch/arm/include/asm/efi.h
arch/arm/include/asm/uaccess-asm.h [new file with mode: 0644]
arch/arm/kernel/armksyms.c
arch/arm/kernel/atags_proc.c
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-header.S
arch/arm/kernel/module.c
arch/arm/kernel/ptrace.c
arch/arm/kernel/sys_oabi-compat.c
arch/arm/mm/proc-macros.S
arch/arm/tools/syscall.tbl
arch/arm64/Kconfig
arch/arm64/Makefile
arch/arm64/boot/dts/mediatek/mt8173.dtsi
arch/arm64/crypto/aes-glue.c
arch/arm64/crypto/crct10dif-ce-core.S
arch/arm64/crypto/sha256-glue.c
arch/arm64/crypto/sha512-glue.c
arch/arm64/include/asm/asm_pointer_auth.h
arch/arm64/include/asm/assembler.h
arch/arm64/include/asm/cacheflush.h
arch/arm64/include/asm/compiler.h
arch/arm64/include/asm/cpu.h
arch/arm64/include/asm/cpucaps.h
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/debug-monitors.h
arch/arm64/include/asm/efi.h
arch/arm64/include/asm/elf.h
arch/arm64/include/asm/esr.h
arch/arm64/include/asm/exception.h
arch/arm64/include/asm/hardirq.h
arch/arm64/include/asm/hwcap.h
arch/arm64/include/asm/insn.h
arch/arm64/include/asm/kvm_emulate.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/kvm_hyp.h
arch/arm64/include/asm/kvm_mmu.h
arch/arm64/include/asm/linkage.h
arch/arm64/include/asm/mman.h [new file with mode: 0644]
arch/arm64/include/asm/pgtable-hwdef.h
arch/arm64/include/asm/pgtable-prot.h
arch/arm64/include/asm/pgtable.h
arch/arm64/include/asm/ptrace.h
arch/arm64/include/asm/scs.h [new file with mode: 0644]
arch/arm64/include/asm/smp.h
arch/arm64/include/asm/stacktrace.h
arch/arm64/include/asm/suspend.h
arch/arm64/include/asm/sysreg.h
arch/arm64/include/asm/thread_info.h
arch/arm64/include/asm/unistd.h
arch/arm64/include/asm/unistd32.h
arch/arm64/include/asm/vmap_stack.h
arch/arm64/include/uapi/asm/hwcap.h
arch/arm64/include/uapi/asm/mman.h [new file with mode: 0644]
arch/arm64/include/uapi/asm/ptrace.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/asm-offsets.c
arch/arm64/kernel/cpu-reset.S
arch/arm64/kernel/cpu_errata.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/cpuinfo.c
arch/arm64/kernel/crash_core.c
arch/arm64/kernel/debug-monitors.c
arch/arm64/kernel/efi-entry.S
arch/arm64/kernel/efi-header.S
arch/arm64/kernel/efi-rt-wrapper.S
arch/arm64/kernel/entry-common.c
arch/arm64/kernel/entry-fpsimd.S
arch/arm64/kernel/entry-ftrace.S
arch/arm64/kernel/entry.S
arch/arm64/kernel/head.S
arch/arm64/kernel/hibernate-asm.S
arch/arm64/kernel/hyp-stub.S
arch/arm64/kernel/image-vars.h
arch/arm64/kernel/insn.c
arch/arm64/kernel/machine_kexec_file.c
arch/arm64/kernel/paravirt.c
arch/arm64/kernel/probes/decode-insn.c
arch/arm64/kernel/probes/kprobes_trampoline.S
arch/arm64/kernel/process.c
arch/arm64/kernel/ptrace.c
arch/arm64/kernel/reloc_test_syms.S
arch/arm64/kernel/relocate_kernel.S
arch/arm64/kernel/scs.c [new file with mode: 0644]
arch/arm64/kernel/sdei.c
arch/arm64/kernel/signal.c
arch/arm64/kernel/sleep.S
arch/arm64/kernel/smccc-call.S
arch/arm64/kernel/smp.c
arch/arm64/kernel/syscall.c
arch/arm64/kernel/traps.c
arch/arm64/kernel/vdso.c
arch/arm64/kernel/vdso/Makefile
arch/arm64/kernel/vdso/note.S
arch/arm64/kernel/vdso/sigreturn.S
arch/arm64/kernel/vdso/vdso.S
arch/arm64/kernel/vdso32/sigreturn.S
arch/arm64/kernel/vmlinux.lds.S
arch/arm64/kvm/hyp/switch.c
arch/arm64/kvm/hyp/sysreg-sr.c
arch/arm64/kvm/hyp/tlb.c
arch/arm64/kvm/reset.c
arch/arm64/kvm/sys_regs.c
arch/arm64/lib/copy_from_user.S
arch/arm64/lib/copy_in_user.S
arch/arm64/lib/copy_to_user.S
arch/arm64/lib/crc32.S
arch/arm64/lib/memcpy.S
arch/arm64/mm/context.c
arch/arm64/mm/dump.c
arch/arm64/mm/init.c
arch/arm64/mm/mmu.c
arch/arm64/mm/pageattr.c
arch/arm64/mm/proc.S
arch/arm64/net/bpf_jit.h
arch/arm64/net/bpf_jit_comp.c
arch/c6x/lib/checksum.c
arch/csky/abiv1/inc/abi/entry.h
arch/csky/abiv2/inc/abi/entry.h
arch/csky/include/asm/thread_info.h
arch/csky/kernel/entry.S
arch/ia64/include/asm/checksum.h
arch/ia64/include/asm/device.h
arch/ia64/kernel/efi.c
arch/ia64/kernel/syscalls/syscall.tbl
arch/ia64/lib/csum_partial_copy.c
arch/m68k/amiga/config.c
arch/m68k/coldfire/device.c
arch/m68k/coldfire/m5441x.c
arch/m68k/configs/amiga_defconfig
arch/m68k/configs/apollo_defconfig
arch/m68k/configs/atari_defconfig
arch/m68k/configs/bvme6000_defconfig
arch/m68k/configs/hp300_defconfig
arch/m68k/configs/mac_defconfig
arch/m68k/configs/multi_defconfig
arch/m68k/configs/mvme147_defconfig
arch/m68k/configs/mvme16x_defconfig
arch/m68k/configs/q40_defconfig
arch/m68k/configs/sun3_defconfig
arch/m68k/configs/sun3x_defconfig
arch/m68k/include/asm/checksum.h
arch/m68k/include/asm/m5441xsim.h
arch/m68k/include/asm/mac_via.h
arch/m68k/include/asm/mcfclk.h
arch/m68k/include/asm/uaccess_mm.h
arch/m68k/kernel/syscalls/syscall.tbl
arch/m68k/lib/checksum.c
arch/m68k/mac/config.c
arch/m68k/mac/iop.c
arch/m68k/mac/via.c
arch/m68k/tools/amiga/dmesg.c
arch/microblaze/kernel/microblaze_ksyms.c
arch/microblaze/kernel/syscalls/syscall.tbl
arch/mips/cavium-octeon/crypto/octeon-md5.c
arch/mips/kernel/syscalls/syscall_n32.tbl
arch/mips/kernel/syscalls/syscall_n64.tbl
arch/mips/kernel/syscalls/syscall_o32.tbl
arch/nios2/include/asm/checksum.h
arch/parisc/include/asm/checksum.h
arch/parisc/include/asm/pgtable.h
arch/parisc/kernel/perf.c
arch/parisc/kernel/syscalls/syscall.tbl
arch/parisc/lib/checksum.c
arch/parisc/mm/init.c
arch/powerpc/Kconfig
arch/powerpc/crypto/md5-glue.c
arch/powerpc/crypto/sha1-spe-glue.c
arch/powerpc/crypto/sha1.c
arch/powerpc/crypto/sha256-spe-glue.c
arch/powerpc/include/asm/io.h
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/include/uapi/asm/kvm_para.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/irq.c
arch/powerpc/kernel/isa-bridge.c
arch/powerpc/kernel/nvram_64.c
arch/powerpc/kernel/pci_64.c
arch/powerpc/kernel/syscalls/syscall.tbl
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/vmlinux.lds.S
arch/powerpc/mm/ioremap_64.c
arch/powerpc/perf/imc-pmu.c
arch/powerpc/platforms/cell/spufs/coredump.c
arch/powerpc/platforms/cell/spufs/file.c
arch/powerpc/platforms/cell/spufs/spufs.h
arch/riscv/include/asm/pgtable.h
arch/riscv/mm/ptdump.c
arch/s390/crypto/sha1_s390.c
arch/s390/include/asm/checksum.h
arch/s390/kernel/setup.c
arch/s390/kernel/syscalls/syscall.tbl
arch/sh/Kconfig
arch/sh/include/asm/checksum_32.h
arch/sh/kernel/cpu/sh4/sq.c
arch/sh/kernel/syscalls/syscall.tbl
arch/sh/kernel/traps.c
arch/sparc/crypto/md5_glue.c
arch/sparc/crypto/sha1_glue.c
arch/sparc/crypto/sha256_glue.c
arch/sparc/crypto/sha512_glue.c
arch/sparc/include/asm/checksum.h
arch/sparc/include/asm/checksum_32.h
arch/sparc/include/asm/checksum_64.h
arch/sparc/kernel/syscalls/syscall.tbl
arch/unicore32/kernel/ksyms.c
arch/x86/Kconfig
arch/x86/Kconfig.debug
arch/x86/Makefile
arch/x86/boot/Makefile
arch/x86/boot/compressed/acpi.c
arch/x86/boot/compressed/efi_thunk_64.S
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/vmlinux.lds.S
arch/x86/boot/string.c
arch/x86/boot/string.h
arch/x86/crypto/aesni-intel_asm.S
arch/x86/crypto/camellia-aesni-avx-asm_64.S
arch/x86/crypto/camellia-aesni-avx2-asm_64.S
arch/x86/crypto/crc32c-pcl-intel-asm_64.S
arch/x86/crypto/sha1_ssse3_glue.c
arch/x86/crypto/sha256_ssse3_glue.c
arch/x86/crypto/sha512_ssse3_glue.c
arch/x86/entry/entry_32.S
arch/x86/entry/entry_64.S
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
arch/x86/entry/vdso/Makefile
arch/x86/entry/vdso/vdso2c.c
arch/x86/entry/vdso/vdso2c.h
arch/x86/events/Kconfig
arch/x86/events/Makefile
arch/x86/events/core.c
arch/x86/events/intel/Makefile
arch/x86/events/intel/bts.c
arch/x86/events/intel/core.c
arch/x86/events/intel/pt.c
arch/x86/events/intel/uncore.h
arch/x86/events/perf_event.h
arch/x86/events/probe.c
arch/x86/events/rapl.c [moved from arch/x86/events/intel/rapl.c with 91% similarity]
arch/x86/events/zhaoxin/Makefile [new file with mode: 0644]
arch/x86/events/zhaoxin/core.c [new file with mode: 0644]
arch/x86/hyperv/hv_init.c
arch/x86/ia32/audit.c
arch/x86/ia32/ia32_signal.c
arch/x86/include/asm/GEN-for-each-reg.h [new file with mode: 0644]
arch/x86/include/asm/apb_timer.h
arch/x86/include/asm/archrandom.h
arch/x86/include/asm/asm-prototypes.h
arch/x86/include/asm/audit.h [new file with mode: 0644]
arch/x86/include/asm/checksum.h
arch/x86/include/asm/checksum_32.h
arch/x86/include/asm/checksum_64.h
arch/x86/include/asm/compat.h
arch/x86/include/asm/cpu_device_id.h
arch/x86/include/asm/device.h
arch/x86/include/asm/dma.h
arch/x86/include/asm/doublefault.h
arch/x86/include/asm/efi.h
arch/x86/include/asm/fpu/internal.h
arch/x86/include/asm/fpu/xstate.h
arch/x86/include/asm/intel-mid.h
arch/x86/include/asm/intel_pmc_ipc.h [deleted file]
arch/x86/include/asm/intel_scu_ipc.h
arch/x86/include/asm/intel_scu_ipc_legacy.h [new file with mode: 0644]
arch/x86/include/asm/intel_telemetry.h
arch/x86/include/asm/invpcid.h
arch/x86/include/asm/io_bitmap.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/mmzone_32.h
arch/x86/include/asm/msr-index.h
arch/x86/include/asm/nospec-branch.h
arch/x86/include/asm/orc_types.h
arch/x86/include/asm/pgtable-2level_types.h
arch/x86/include/asm/pgtable-3level_types.h
arch/x86/include/asm/pgtable_32.h
arch/x86/include/asm/pgtable_64_types.h
arch/x86/include/asm/pgtable_types.h
arch/x86/include/asm/processor.h
arch/x86/include/asm/resctrl.h [moved from arch/x86/include/asm/resctrl_sched.h with 92% similarity]
arch/x86/include/asm/smap.h
arch/x86/include/asm/spinlock_types.h
arch/x86/include/asm/switch_to.h
arch/x86/include/asm/traps.h
arch/x86/include/asm/unwind_hints.h
arch/x86/include/asm/uv/bios.h
arch/x86/include/asm/uv/uv.h
arch/x86/include/asm/uv/uv_hub.h
arch/x86/include/asm/uv/uv_mmrs.h
arch/x86/include/uapi/asm/unistd.h
arch/x86/kernel/Makefile
arch/x86/kernel/apb_timer.c
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/apic/x2apic_uv_x.c
arch/x86/kernel/audit_64.c
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/intel.c
arch/x86/kernel/cpu/match.c
arch/x86/kernel/cpu/mce/core.c
arch/x86/kernel/cpu/mce/p5.c
arch/x86/kernel/cpu/mce/winchip.c
arch/x86/kernel/cpu/microcode/core.c
arch/x86/kernel/cpu/perfctr-watchdog.c
arch/x86/kernel/cpu/resctrl/core.c
arch/x86/kernel/cpu/resctrl/ctrlmondata.c
arch/x86/kernel/cpu/resctrl/internal.h
arch/x86/kernel/cpu/resctrl/monitor.c
arch/x86/kernel/cpu/resctrl/pseudo_lock.c
arch/x86/kernel/cpu/resctrl/rdtgroup.c
arch/x86/kernel/dumpstack_32.c
arch/x86/kernel/e820.c
arch/x86/kernel/early_printk.c
arch/x86/kernel/fpu/core.c
arch/x86/kernel/fpu/init.c
arch/x86/kernel/fpu/regset.c
arch/x86/kernel/fpu/signal.c
arch/x86/kernel/fpu/xstate.c
arch/x86/kernel/ftrace.c
arch/x86/kernel/ftrace_32.S
arch/x86/kernel/ftrace_64.S
arch/x86/kernel/ioport.c
arch/x86/kernel/irq_64.c
arch/x86/kernel/nmi.c
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/setup.c
arch/x86/kernel/setup_percpu.c
arch/x86/kernel/signal.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/tboot.c
arch/x86/kernel/traps.c
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/svm/sev.c
arch/x86/lib/checksum_32.S
arch/x86/lib/csum-wrappers_64.c
arch/x86/lib/retpoline.S
arch/x86/mm/cpu_entry_area.c
arch/x86/mm/dump_pagetables.c
arch/x86/mm/fault.c
arch/x86/mm/init.c
arch/x86/mm/init_64.c
arch/x86/mm/mmap.c
arch/x86/mm/numa_32.c
arch/x86/mm/pti.c
arch/x86/mm/tlb.c
arch/x86/platform/efi/efi.c
arch/x86/platform/efi/efi_stub_64.S
arch/x86/platform/uv/bios_uv.c
arch/x86/platform/uv/uv_sysfs.c
arch/x86/power/cpu.c
arch/x86/um/asm/checksum.h
arch/x86/xen/efi.c
arch/xtensa/include/asm/checksum.h
arch/xtensa/kernel/syscalls/syscall.tbl
block/blk-core.c
crypto/Kconfig
crypto/algapi.c
crypto/algif_rng.c
crypto/api.c
crypto/blake2b_generic.c
crypto/crypto_engine.c
crypto/drbg.c
crypto/essiv.c
crypto/internal.h
crypto/jitterentropy-kcapi.c
crypto/jitterentropy.c
crypto/lrw.c
crypto/sha1_generic.c
crypto/sha256_generic.c
crypto/shash.c
crypto/xts.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpi_tad.c
drivers/acpi/apei/ghes.c
drivers/acpi/arm64/gtdt.c
drivers/acpi/arm64/iort.c
drivers/acpi/device_pm.c
drivers/acpi/ec.c
drivers/acpi/sleep.c
drivers/ata/libata-core.c
drivers/base/core.c
drivers/base/node.c
drivers/base/platform.c
drivers/base/power/main.c
drivers/base/power/runtime.c
drivers/base/power/sysfs.c
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap-i2c.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap.c
drivers/base/swnode.c
drivers/block/drbd/drbd_bitmap.c
drivers/block/loop.c
drivers/block/zram/zcomp.c
drivers/block/zram/zcomp.h
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmtksdio.c
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/cctrng.c [new file with mode: 0644]
drivers/char/hw_random/cctrng.h [new file with mode: 0644]
drivers/char/hw_random/omap-rng.c
drivers/char/hw_random/optee-rng.c
drivers/char/hw_random/xgene-rng.c
drivers/char/ipmi/Kconfig
drivers/char/ipmi/ipmi_si_hotmod.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/char/nvram.c
drivers/char/pcmcia/cm4000_cs.c
drivers/char/random.c
drivers/char/tpm/eventlog/tpm2.c
drivers/char/tpm/tpm_ftpm_tee.c
drivers/clk/clk-qoriq.c
drivers/clk/clk.c
drivers/clk/qcom/Kconfig
drivers/clk/qcom/gcc-sm8150.c
drivers/connector/cn_proc.c
drivers/cpufreq/Kconfig
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/cpufreq-dt-platdev.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/imx-cpufreq-dt.c
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/qcom-cpufreq-nvmem.c
drivers/cpufreq/qoriq-cpufreq.c
drivers/cpuidle/Kconfig.arm
drivers/cpuidle/Makefile
drivers/cpuidle/cpuidle-psci.c
drivers/cpuidle/cpuidle-qcom-spm.c [moved from drivers/soc/qcom/spm.c with 75% similarity]
drivers/cpuidle/sysfs.c
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-cipher.c
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c
drivers/crypto/amlogic/amlogic-gxl-core.c
drivers/crypto/atmel-sha.c
drivers/crypto/axis/artpec6_crypto.c
drivers/crypto/bcm/cipher.c
drivers/crypto/cavium/nitrox/nitrox_main.c
drivers/crypto/ccp/Kconfig
drivers/crypto/ccp/ccp-crypto-sha.c
drivers/crypto/ccp/sev-dev.c
drivers/crypto/ccree/cc_cipher.c
drivers/crypto/ccree/cc_debugfs.c
drivers/crypto/chelsio/chcr_algo.c
drivers/crypto/chelsio/chcr_ipsec.c
drivers/crypto/chelsio/chtls/chtls_io.c
drivers/crypto/hisilicon/Kconfig
drivers/crypto/hisilicon/hpre/hpre.h
drivers/crypto/hisilicon/hpre/hpre_crypto.c
drivers/crypto/hisilicon/hpre/hpre_main.c
drivers/crypto/hisilicon/qm.c
drivers/crypto/hisilicon/qm.h
drivers/crypto/hisilicon/sec2/sec.h
drivers/crypto/hisilicon/sec2/sec_crypto.c
drivers/crypto/hisilicon/sec2/sec_main.c
drivers/crypto/hisilicon/zip/zip.h
drivers/crypto/hisilicon/zip/zip_crypto.c
drivers/crypto/hisilicon/zip/zip_main.c
drivers/crypto/marvell/octeontx/otx_cptpf_main.c
drivers/crypto/marvell/octeontx/otx_cptpf_mbox.c
drivers/crypto/marvell/octeontx/otx_cptpf_ucode.c
drivers/crypto/marvell/octeontx/otx_cptvf_algs.c
drivers/crypto/marvell/octeontx/otx_cptvf_main.c
drivers/crypto/marvell/octeontx/otx_cptvf_reqmgr.c
drivers/crypto/mediatek/mtk-sha.c
drivers/crypto/n2_core.c
drivers/crypto/omap-sham.c
drivers/crypto/s5p-sss.c
drivers/crypto/stm32/stm32-crc32.c
drivers/crypto/stm32/stm32-hash.c
drivers/dax/device.c
drivers/devfreq/Kconfig
drivers/devfreq/Makefile
drivers/devfreq/devfreq.c
drivers/devfreq/imx-bus.c [new file with mode: 0644]
drivers/devfreq/tegra30-devfreq.c
drivers/edac/amd64_edac.c
drivers/edac/amd8131_edac.c
drivers/edac/armada_xp_edac.c
drivers/edac/i10nm_base.c
drivers/edac/skx_base.c
drivers/edac/skx_common.c
drivers/edac/skx_common.h
drivers/edac/thunderx_edac.c
drivers/edac/xgene_edac.c
drivers/firewire/core-cdev.c
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/arm_sdei.c
drivers/firmware/efi/Kconfig
drivers/firmware/efi/arm-init.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/efivars.c
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/alignedmem.c [new file with mode: 0644]
drivers/firmware/efi/libstub/arm32-stub.c
drivers/firmware/efi/libstub/arm64-stub.c
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/efi-stub.c [moved from drivers/firmware/efi/libstub/arm-stub.c with 83% similarity]
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/fdt.c
drivers/firmware/efi/libstub/file.c
drivers/firmware/efi/libstub/gop.c
drivers/firmware/efi/libstub/mem.c
drivers/firmware/efi/libstub/pci.c
drivers/firmware/efi/libstub/randomalloc.c
drivers/firmware/efi/libstub/relocate.c [new file with mode: 0644]
drivers/firmware/efi/libstub/secureboot.c
drivers/firmware/efi/libstub/tpm.c
drivers/firmware/efi/libstub/vsprintf.c [new file with mode: 0644]
drivers/firmware/efi/libstub/x86-stub.c
drivers/firmware/efi/test/efi_test.c
drivers/firmware/psci/psci.c
drivers/firmware/raspberrypi.c
drivers/firmware/smccc/Kconfig [new file with mode: 0644]
drivers/firmware/smccc/Makefile [new file with mode: 0644]
drivers/firmware/smccc/smccc.c [new file with mode: 0644]
drivers/fpga/dfl-afu-dma-region.c
drivers/fpga/dfl-fme-pr.c
drivers/gpio/gpio-bcm-kona.c
drivers/gpio/gpio-exar.c
drivers/gpio/gpio-mlxbf2.c
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-pxa.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
drivers/gpu/drm/amd/amdkfd/kfd_priv.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
drivers/gpu/drm/armada/armada_drv.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_file.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_scatter.c
drivers/gpu/drm/etnaviv/etnaviv_drv.c
drivers/gpu/drm/etnaviv/etnaviv_dump.c
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
drivers/gpu/drm/i915/gem/i915_gem_userptr.c
drivers/gpu/drm/i915/gem/selftests/mock_dmabuf.c
drivers/gpu/drm/i915/i915_perf.c
drivers/gpu/drm/i915/intel_runtime_pm.c
drivers/gpu/drm/ingenic/ingenic-drm.c
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
drivers/gpu/drm/radeon/radeon_kms.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adt7411.c
drivers/hwmon/amd_energy.c [new file with mode: 0644]
drivers/hwmon/applesmc.c
drivers/hwmon/bt1-pvt.c [new file with mode: 0644]
drivers/hwmon/bt1-pvt.h [new file with mode: 0644]
drivers/hwmon/dell-smm-hwmon.c
drivers/hwmon/gsc-hwmon.c [new file with mode: 0644]
drivers/hwmon/hwmon.c
drivers/hwmon/ina2xx.c
drivers/hwmon/lm70.c
drivers/hwmon/lm75.c
drivers/hwmon/lm75.h
drivers/hwmon/lm90.c
drivers/hwmon/nct6775.c
drivers/hwmon/nct7802.c
drivers/hwmon/nct7904.c
drivers/hwmon/pmbus/Kconfig
drivers/hwmon/pmbus/Makefile
drivers/hwmon/pmbus/max16601.c [new file with mode: 0644]
drivers/hwmon/pmbus/pmbus_core.c
drivers/hwtracing/coresight/Kconfig
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i3c/master.c
drivers/iio/dac/ad5761.c
drivers/iio/dac/ti-dac7612.c
drivers/infiniband/core/rdma_core.c
drivers/infiniband/hw/hfi1/user_exp_rcv.c
drivers/infiniband/hw/mlx5/mr.c
drivers/infiniband/hw/qib/qib_sysfs.c
drivers/infiniband/hw/vmw_pvrdma/pvrdma_main.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_ib.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/input/evdev.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/applespi.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/dlink-dir685-touchkeys.c
drivers/input/misc/axp20x-pek.c
drivers/input/mouse/synaptics.c
drivers/input/rmi4/rmi_driver.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/mms114.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/iommu/iommu.c
drivers/leds/leds-pca963x.c
drivers/lightnvm/pblk-init.c
drivers/macintosh/via-pmu.c
drivers/md/dm-bufio.c
drivers/md/md-bitmap.c
drivers/media/cec/cec-notifier.c
drivers/media/common/videobuf2/videobuf2-dma-sg.c
drivers/media/common/videobuf2/videobuf2-vmalloc.c
drivers/media/i2c/imx214.c
drivers/media/mmc/siano/smssdio.c
drivers/media/pci/ivtv/ivtv-udma.c
drivers/media/pci/ivtv/ivtv-yuv.c
drivers/media/pci/ivtv/ivtvfb.c
drivers/media/usb/pulse8-cec/pulse8-cec.c
drivers/media/v4l2-core/v4l2-fwnode.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/gateworks-gsc.c [new file with mode: 0644]
drivers/mfd/intel_pmc_bxt.c [new file with mode: 0644]
drivers/mfd/intel_soc_pmic_bxtwc.c
drivers/mfd/intel_soc_pmic_mrfld.c
drivers/misc/mei/pci-me.c
drivers/misc/mei/pci-txe.c
drivers/misc/vmw_vmci/vmci_host.c
drivers/mmc/core/block.c
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/debugfs.c
drivers/mmc/core/mmc.c
drivers/mmc/core/quirks.h
drivers/mmc/core/regulator.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/android-goldfish.c
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/au1xmmc.c
drivers/mmc/host/bcm2835.c
drivers/mmc/host/cavium.c
drivers/mmc/host/cb710-mmc.c
drivers/mmc/host/dw_mmc-k3.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/jz4740_mmc.c
drivers/mmc/host/meson-gx-mmc.c
drivers/mmc/host/meson-mx-sdhc-clkc.c [new file with mode: 0644]
drivers/mmc/host/meson-mx-sdhc-mmc.c [new file with mode: 0644]
drivers/mmc/host/meson-mx-sdhc.h [new file with mode: 0644]
drivers/mmc/host/meson-mx-sdio.c
drivers/mmc/host/mmc_hsq.c
drivers/mmc/host/mmc_hsq.h
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/mmci.c
drivers/mmc/host/mmci_stm32_sdmmc.c
drivers/mmc/host/mtk-sd.c
drivers/mmc/host/mvsdio.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/owl-mmc.c
drivers/mmc/host/renesas_sdhi.h
drivers/mmc/host/renesas_sdhi_core.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mmc/host/rtsx_usb_sdmmc.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-cadence.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc-mcf.c [new file with mode: 0644]
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-msm.c
drivers/mmc/host/sdhci-of-arasan.c
drivers/mmc/host/sdhci-of-at91.c
drivers/mmc/host/sdhci-of-dwcmshc.c
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci-core.c
drivers/mmc/host/sdhci-pci-gli.c
drivers/mmc/host/sdhci-pci-o2micro.c
drivers/mmc/host/sdhci-pci.h
drivers/mmc/host/sdhci-sprd.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sdricoh_cs.c
drivers/mmc/host/sunxi-mmc.c
drivers/mmc/host/tifm_sd.c
drivers/mmc/host/tmio_mmc_core.c
drivers/mmc/host/uniphier-sd.c
drivers/mmc/host/usdhi6rol0.c
drivers/mmc/host/via-sdmmc.c
drivers/mmc/host/wbsd.c
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/mtdpstore.c [new file with mode: 0644]
drivers/mtd/ubi/io.c
drivers/net/bonding/bond_sysfs_slave.c
drivers/net/dsa/ocelot/felix.c
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/mellanox/mlx5/core/Kconfig
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/port.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/netronome/nfp/flower/offload.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/phy/swphy.c
drivers/net/usb/qmi_wwan.c
drivers/net/wireless/ath/ath10k/sdio.c
drivers/net/wireless/ath/ath10k/sdio.h
drivers/net/wireless/ath/ath6kl/hif.h
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/broadcom/b43/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/marvell/libertas/if_sdio.c
drivers/net/wireless/marvell/mwifiex/sdio.c
drivers/nfc/s3fwrn5/firmware.c
drivers/nfc/st21nfca/dep.c
drivers/nvme/host/pci.c
drivers/oprofile/event_buffer.c
drivers/pci/hotplug/pciehp_core.c
drivers/pci/pci-driver.c
drivers/pci/pcie/portdrv_pci.c
drivers/pcmcia/electra_cf.c
drivers/perf/Kconfig
drivers/perf/arm_dsu_pmu.c
drivers/perf/arm_smmuv3_pmu.c
drivers/perf/arm_spe_pmu.c
drivers/perf/hisilicon/Kconfig [new file with mode: 0644]
drivers/perf/hisilicon/Makefile
drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c
drivers/perf/hisilicon/hisi_uncore_hha_pmu.c
drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c
drivers/perf/hisilicon/hisi_uncore_pmu.c
drivers/platform/chrome/chromeos_pstore.c
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dcdbas.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel-vbtn.c
drivers/platform/x86/intel-wmi-sbl-fw-update.c [new file with mode: 0644]
drivers/platform/x86/intel_cht_int33fe_typec.c
drivers/platform/x86/intel_mid_powerbtn.c
drivers/platform/x86/intel_pmc_ipc.c [deleted file]
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/intel_scu_ipcutil.c
drivers/platform/x86/intel_scu_pcidrv.c [new file with mode: 0644]
drivers/platform/x86/intel_scu_pltdrv.c [new file with mode: 0644]
drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
drivers/platform/x86/intel_telemetry_core.c
drivers/platform/x86/intel_telemetry_debugfs.c
drivers/platform/x86/intel_telemetry_pltdrv.c
drivers/platform/x86/lg-laptop.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/toshiba_acpi.c
drivers/platform/x86/touchscreen_dmi.c
drivers/platform/x86/wmi.c
drivers/power/supply/bd70528-charger.c
drivers/powercap/intel_rapl_common.c
drivers/regulator/88pg86x.c
drivers/regulator/88pm800-regulator.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/ab8500.c
drivers/regulator/act8865-regulator.c
drivers/regulator/act8945a-regulator.c
drivers/regulator/arizona-ldo1.c
drivers/regulator/arizona-micsupp.c
drivers/regulator/as3711-regulator.c
drivers/regulator/as3722-regulator.c
drivers/regulator/axp20x-regulator.c
drivers/regulator/bcm590xx-regulator.c
drivers/regulator/bd70528-regulator.c
drivers/regulator/bd71828-regulator.c
drivers/regulator/bd718x7-regulator.c
drivers/regulator/core.c
drivers/regulator/da903x.c
drivers/regulator/db8500-prcmu.c
drivers/regulator/helpers.c
drivers/regulator/hi6421-regulator.c
drivers/regulator/lochnagar-regulator.c
drivers/regulator/lp873x-regulator.c
drivers/regulator/lp87565-regulator.c
drivers/regulator/lp8788-buck.c
drivers/regulator/max77650-regulator.c
drivers/regulator/max77826-regulator.c [new file with mode: 0644]
drivers/regulator/max8998.c
drivers/regulator/mcp16502.c
drivers/regulator/mp8859.c
drivers/regulator/mt6323-regulator.c
drivers/regulator/mt6358-regulator.c
drivers/regulator/mt6380-regulator.c
drivers/regulator/mt6397-regulator.c
drivers/regulator/palmas-regulator.c
drivers/regulator/qcom-rpmh-regulator.c
drivers/regulator/qcom_rpm-regulator.c
drivers/regulator/qcom_smd-regulator.c
drivers/regulator/rk808-regulator.c
drivers/regulator/s2mps11.c
drivers/regulator/sky81452-regulator.c
drivers/regulator/stpmic1_regulator.c
drivers/regulator/tps65086-regulator.c
drivers/regulator/tps65217-regulator.c
drivers/regulator/tps65218-regulator.c
drivers/regulator/tps65912-regulator.c
drivers/regulator/tps80031-regulator.c
drivers/regulator/twl-regulator.c
drivers/regulator/twl6030-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8350-regulator.c
drivers/regulator/wm8400-regulator.c
drivers/scsi/esas2r/esas2r_ioctl.c
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/sd_zbc.c
drivers/soc/mediatek/mtk-cmdq-helper.c
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-amd.c [new file with mode: 0644]
drivers/spi/spi-armada-3700.c
drivers/spi/spi-atmel.c
drivers/spi/spi-axi-spi-engine.c
drivers/spi/spi-bcm-qspi.c
drivers/spi/spi-bcm2835.c
drivers/spi/spi-bcm2835aux.c
drivers/spi/spi-dw-core.c [moved from drivers/spi/spi-dw.c with 70% similarity]
drivers/spi/spi-dw-dma.c [new file with mode: 0644]
drivers/spi/spi-dw-mid.c [deleted file]
drivers/spi/spi-dw-mmio.c
drivers/spi/spi-dw-pci.c
drivers/spi/spi-dw.h
drivers/spi/spi-ep93xx.c
drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-fsl-lpspi.c
drivers/spi/spi-fsl-qspi.c
drivers/spi/spi-fsl-spi.c
drivers/spi/spi-hisi-sfc-v3xx.c
drivers/spi/spi-imx.c
drivers/spi/spi-mem.c
drivers/spi/spi-mtk-nor.c
drivers/spi/spi-mux.c
drivers/spi/spi-orion.c
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-rb4xx.c
drivers/spi/spi-rockchip.c
drivers/spi/spi-sc18is602.c
drivers/spi/spi-sh-msiof.c
drivers/spi/spi-sprd-adi.c
drivers/spi/spi-stm32-qspi.c
drivers/spi/spi-stm32.c
drivers/spi/spi-sun6i.c
drivers/spi/spi-tegra114.c
drivers/spi/spi-tegra20-sflash.c
drivers/spi/spi-tegra20-slink.c
drivers/spi/spi-uniphier.c
drivers/spi/spi.c
drivers/spi/spidev.c
drivers/staging/android/ion/ion_heap.c
drivers/staging/greybus/sdio.c
drivers/staging/media/ipu3/ipu3-css-pool.h
drivers/staging/media/ipu3/ipu3-dmamap.c
drivers/tty/n_hdlc.c
drivers/usb/core/devices.c
drivers/usb/core/devio.c
drivers/usb/gadget/function/f_hid.c
drivers/usb/typec/mux/Kconfig
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/typec/tcpm/Kconfig
drivers/video/fbdev/amifb.c
drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c
drivers/watchdog/Kconfig
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/intel-mid_wdt.c
fs/Kconfig
fs/Kconfig.binfmt
fs/adfs/Kconfig
fs/affs/Kconfig
fs/afs/Kconfig
fs/aio.c
fs/bfs/Kconfig
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c
fs/block_dev.c
fs/btrfs/disk-io.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/inode.c
fs/btrfs/send.c
fs/buffer.c
fs/cachefiles/Kconfig
fs/ceph/caps.c
fs/char_dev.c
fs/coda/Kconfig
fs/compat_binfmt_elf.c
fs/configfs/inode.c
fs/configfs/item.c
fs/cramfs/Kconfig
fs/crypto/crypto.c
fs/crypto/fname.c
fs/crypto/fscrypt_private.h
fs/crypto/hkdf.c
fs/crypto/hooks.c
fs/crypto/keyring.c
fs/crypto/keysetup.c
fs/crypto/policy.c
fs/ecryptfs/Kconfig
fs/ecryptfs/crypto.c
fs/erofs/data.c
fs/erofs/decompressor.c
fs/erofs/zdata.c
fs/exfat/inode.c
fs/ext2/inode.c
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/readpage.c
fs/ext4/super.c
fs/ext4/sysfs.c
fs/ext4/verity.c
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/hash.c
fs/f2fs/super.c
fs/f2fs/sysfs.c
fs/f2fs/verity.c
fs/fat/Kconfig
fs/fat/dir.c
fs/fat/inode.c
fs/file_table.c
fs/fs-writeback.c
fs/fs_context.c
fs/fscache/Kconfig
fs/fscache/cache.c
fs/fscache/cookie.c
fs/fscache/object.c
fs/fscache/operation.c
fs/fuse/Kconfig
fs/fuse/dev.c
fs/fuse/file.c
fs/gfs2/aops.c
fs/gfs2/dir.c
fs/gfs2/lops.c
fs/gfs2/quota.c
fs/hfs/Kconfig
fs/hpfs/Kconfig
fs/hpfs/file.c
fs/inode.c
fs/internal.h
fs/iomap/buffered-io.c
fs/iomap/trace.h
fs/isofs/Kconfig
fs/isofs/inode.c
fs/jfs/inode.c
fs/locks.c
fs/mount.h
fs/mpage.c
fs/namei.c
fs/namespace.c
fs/nfs/blocklayout/extent_tree.c
fs/nfs/internal.h
fs/nfs/write.c
fs/nfsd/nfs4recover.c
fs/nfsd/vfs.c
fs/nilfs2/inode.c
fs/notify/fanotify/fanotify.c
fs/notify/inotify/Kconfig
fs/ntfs/Kconfig
fs/ntfs/aops.c
fs/ntfs/malloc.h
fs/ntfs/mft.c
fs/ocfs2/Kconfig
fs/ocfs2/aops.c
fs/ocfs2/dlm/dlmmaster.c
fs/ocfs2/dlmfs/dlmfs.c
fs/ocfs2/ocfs2.h
fs/ocfs2/slot_map.c
fs/ocfs2/super.c
fs/omfs/file.c
fs/open.c
fs/orangefs/inode.c
fs/overlayfs/Kconfig
fs/proc/Kconfig
fs/proc/meminfo.c
fs/proc/task_mmu.c
fs/proc_namespace.c
fs/pstore/Kconfig
fs/pstore/Makefile
fs/pstore/blk.c [new file with mode: 0644]
fs/pstore/ftrace.c
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
fs/pstore/ram.c
fs/pstore/ram_core.c
fs/pstore/zone.c [new file with mode: 0644]
fs/qnx6/inode.c
fs/readdir.c
fs/reiserfs/inode.c
fs/romfs/Kconfig
fs/squashfs/block.c
fs/squashfs/decompressor.h
fs/squashfs/decompressor_multi.c
fs/squashfs/decompressor_multi_percpu.c
fs/squashfs/decompressor_single.c
fs/squashfs/lz4_wrapper.c
fs/squashfs/lzo_wrapper.c
fs/squashfs/squashfs.h
fs/squashfs/xz_wrapper.c
fs/squashfs/zlib_wrapper.c
fs/squashfs/zstd_wrapper.c
fs/stat.c
fs/sync.c
fs/sysfs/dir.c
fs/sysfs/file.c
fs/sysfs/mount.c
fs/sysfs/symlink.c
fs/sysv/Kconfig
fs/ubifs/auth.c
fs/ubifs/debug.c
fs/ubifs/lprops.c
fs/ubifs/lpt_commit.c
fs/ubifs/master.c
fs/ubifs/orphan.c
fs/ubifs/replay.c
fs/udf/Kconfig
fs/udf/inode.c
fs/utimes.c
fs/verity/enable.c
fs/verity/fsverity_private.h
fs/verity/measure.c
fs/verity/open.c
fs/verity/signature.c
fs/verity/verify.c
fs/xattr.c
fs/xfs/kmem.c
fs/xfs/xfs_aops.c
fs/xfs/xfs_buf.c
fs/zonefs/super.c
include/asm-generic/5level-fixup.h
include/asm-generic/checksum.h
include/asm-generic/pgtable.h
include/asm-generic/sections.h
include/asm-generic/topology.h
include/asm-generic/vmlinux.lds.h
include/crypto/acompress.h
include/crypto/algapi.h
include/crypto/drbg.h
include/crypto/engine.h
include/crypto/hash.h
include/crypto/sha.h
include/crypto/sha256_base.h
include/linux/arm-smccc.h
include/linux/buffer_head.h
include/linux/capability.h
include/linux/compat.h
include/linux/compiler-clang.h
include/linux/compiler.h
include/linux/compiler_types.h
include/linux/configfs.h
include/linux/console.h
include/linux/cpu.h
include/linux/cpufreq.h
include/linux/crash_dump.h
include/linux/cryptohash.h [deleted file]
include/linux/device_cgroup.h
include/linux/efi.h
include/linux/elf.h
include/linux/fanotify.h
include/linux/filter.h
include/linux/frame.h
include/linux/fs.h
include/linux/fs_context.h
include/linux/fscache-cache.h
include/linux/fscache.h
include/linux/fscrypt.h
include/linux/fsverity.h
include/linux/ftrace_irq.h
include/linux/hardirq.h
include/linux/hwmon.h
include/linux/idr.h
include/linux/ieee80211.h
include/linux/input/lm8333.h
include/linux/iomap.h
include/linux/kmsg_dump.h
include/linux/kobject.h
include/linux/kobject_ns.h
include/linux/linear_range.h [new file with mode: 0644]
include/linux/linkage.h
include/linux/local_lock.h [new file with mode: 0644]
include/linux/local_lock_internal.h [new file with mode: 0644]
include/linux/lockdep.h
include/linux/lsm_hooks.h
include/linux/memcontrol.h
include/linux/memstick.h
include/linux/mfd/gsc.h [new file with mode: 0644]
include/linux/mfd/intel_pmc_bxt.h [new file with mode: 0644]
include/linux/mfd/intel_soc_pmic.h
include/linux/mfd/max8998.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mmc/card.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h
include/linux/mmc/sdio_ids.h
include/linux/mmzone.h
include/linux/mod_devicetable.h
include/linux/module.h
include/linux/moduleloader.h
include/linux/mount.h
include/linux/mpage.h
include/linux/netfilter/nf_conntrack_pptp.h
include/linux/padata.h
include/linux/page_counter.h
include/linux/pagemap.h
include/linux/parser.h
include/linux/perf_event.h
include/linux/platform_data/ad5761.h
include/linux/platform_data/gsc_hwmon.h [new file with mode: 0644]
include/linux/platform_data/itco_wdt.h
include/linux/platform_data/mmc-esdhc-imx.h
include/linux/platform_data/mmc-esdhc-mcf.h [new file with mode: 0644]
include/linux/platform_data/x86/asus-wmi.h
include/linux/pm.h
include/linux/pm_runtime.h
include/linux/preempt.h
include/linux/printk.h
include/linux/property.h
include/linux/psci.h
include/linux/psp-sev.h
include/linux/pstore.h
include/linux/pstore_blk.h [new file with mode: 0644]
include/linux/pstore_ram.h
include/linux/pstore_zone.h [new file with mode: 0644]
include/linux/ptdump.h
include/linux/radix-tree.h
include/linux/rbtree.h
include/linux/rbtree_augmented.h
include/linux/rculist.h
include/linux/rcupdate.h
include/linux/rcupdate_trace.h [new file with mode: 0644]
include/linux/rcupdate_wait.h
include/linux/rcutiny.h
include/linux/rcutree.h
include/linux/regmap.h
include/linux/regulator/coupler.h
include/linux/regulator/driver.h
include/linux/relay.h
include/linux/sched.h
include/linux/scs.h [new file with mode: 0644]
include/linux/signal.h
include/linux/smp.h
include/linux/spi/spi.h
include/linux/stat.h
include/linux/suspend.h
include/linux/swap.h
include/linux/syscalls.h
include/linux/sysfs.h
include/linux/tboot.h
include/linux/torture.h
include/linux/tpm_eventlog.h
include/linux/uaccess.h
include/linux/virtio_net.h
include/linux/vmalloc.h
include/linux/wait.h
include/linux/watchdog.h
include/linux/zsmalloc.h
include/media/cec-notifier.h
include/net/checksum.h
include/net/espintcp.h
include/net/ip_fib.h
include/net/nexthop.h
include/net/tcp.h
include/net/tls.h
include/rdma/uverbs_std_types.h
include/trace/events/erofs.h
include/trace/events/f2fs.h
include/trace/events/regulator.h
include/trace/events/writeback.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/capability.h
include/uapi/linux/elf.h
include/uapi/linux/ethtool_netlink.h
include/uapi/linux/fcntl.h
include/uapi/linux/firewire-cdev.h
include/uapi/linux/fscrypt.h
include/uapi/linux/kvm.h
include/uapi/linux/mmc/ioctl.h
include/uapi/linux/psp-sev.h
include/uapi/linux/stat.h
include/uapi/linux/xfrm.h
include/uapi/rdma/rdma_user_ioctl_cmds.h
init/do_mounts_initrd.c
init/init_task.c
kernel/Makefile
kernel/bpf/core.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/cgroup/rstat.c
kernel/compat.c
kernel/cpu.c
kernel/crash_dump.c
kernel/dma/remap.c
kernel/events/callchain.c
kernel/events/core.c
kernel/events/internal.h
kernel/exit.c
kernel/fork.c
kernel/futex.c
kernel/groups.c
kernel/kprobes.c
kernel/locking/lockdep.c
kernel/locking/rtmutex.c
kernel/module.c
kernel/notifier.c
kernel/padata.c
kernel/power/Kconfig
kernel/power/Makefile
kernel/power/hibernate.c
kernel/power/power.h
kernel/power/user.c
kernel/printk/console_cmdline.h
kernel/printk/internal.h
kernel/printk/printk.c
kernel/printk/printk_safe.c
kernel/rcu/Kconfig
kernel/rcu/Kconfig.debug
kernel/rcu/rcu.h
kernel/rcu/rcuperf.c
kernel/rcu/rcutorture.c
kernel/rcu/srcutree.c
kernel/rcu/tasks.h [new file with mode: 0644]
kernel/rcu/tree.c
kernel/rcu/tree.h
kernel/rcu/tree_exp.h
kernel/rcu/tree_plugin.h
kernel/rcu/tree_stall.h
kernel/rcu/update.c
kernel/reboot.c
kernel/relay.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/scs.c [new file with mode: 0644]
kernel/signal.c
kernel/smp.c
kernel/sys.c
kernel/trace/Kconfig
kernel/trace/bpf_trace.c
kernel/trace/ftrace.c
kernel/trace/trace.c
lib/Kconfig
lib/Kconfig.debug
lib/Kconfig.ubsan
lib/Makefile
lib/bitmap.c
lib/checksum.c
lib/crypto/chacha.c
lib/crypto/sha256.c
lib/ioremap.c
lib/kobject.c
lib/linear_ranges.c [new file with mode: 0644]
lib/mpi/longlong.h
lib/radix-tree.c
lib/sha1.c
lib/strncpy_from_user.c
lib/strnlen_user.c
lib/test_linear_ranges.c [new file with mode: 0644]
lib/test_printf.c
lib/test_vmalloc.c
lib/usercopy.c
lib/vsprintf.c
mm/Kconfig
mm/compaction.c
mm/debug.c
mm/fadvise.c
mm/filemap.c
mm/gup.c
mm/huge_memory.c
mm/internal.h
mm/kasan/Makefile
mm/kasan/common.c
mm/kasan/report.c
mm/khugepaged.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/migrate.c
mm/mm_init.c
mm/nommu.c
mm/page-writeback.c
mm/page_alloc.c
mm/percpu.c
mm/ptdump.c
mm/readahead.c
mm/slab_common.c
mm/slub.c
mm/swap.c
mm/swap_state.c
mm/swapfile.c
mm/util.c
mm/vmalloc.c
mm/vmscan.c
mm/vmstat.c
mm/z3fold.c
mm/zsmalloc.c
net/bluetooth/smp.c
net/bridge/br_multicast.c
net/bridge/netfilter/ebtables.c
net/bridge/netfilter/nft_reject_bridge.c
net/ceph/ceph_common.c
net/ceph/osd_client.c
net/core/neighbour.c
net/core/secure_seq.c
net/dsa/slave.c
net/ipv4/devinet.c
net/ipv4/esp4_offload.c
net/ipv4/fib_frontend.c
net/ipv4/fib_trie.c
net/ipv4/ip_vti.c
net/ipv4/netfilter/nf_nat_pptp.c
net/ipv4/nexthop.c
net/ipv6/addrconf.c
net/ipv6/esp6_offload.c
net/ipv6/seg6_hmac.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/mac80211/mesh_hwmp.c
net/mptcp/crypto.c
net/mptcp/protocol.c
net/netfilter/ipset/ip_set_list_set.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_pptp.c
net/netfilter/nfnetlink_cthelper.c
net/qrtr/ns.c
net/sched/act_ct.c
net/sched/sch_fq_pie.c
net/sctp/Kconfig
net/sctp/auth.c
net/sctp/sm_make_chunk.c
net/sctp/ulpevent.c
net/tls/tls_sw.c
net/vmw_vsock/af_vsock.c
net/vmw_vsock/virtio_transport_common.c
net/wireless/core.c
net/xdp/xdp_umem.c
net/xfrm/espintcp.c
net/xfrm/xfrm_device.c
net/xfrm/xfrm_input.c
net/xfrm/xfrm_interface.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
samples/Kconfig
samples/binderfs/Makefile
samples/ftrace/ftrace-direct-modify.c
samples/ftrace/ftrace-direct-too.c
samples/ftrace/ftrace-direct.c
samples/kprobes/kprobe_example.c
samples/kprobes/kretprobe_example.c
samples/vfs/test-statx.c
scripts/checkpatch.pl
scripts/kernel-doc
scripts/link-vmlinux.sh
scripts/mod/modpost.c
scripts/sphinx-pre-install
security/Makefile
security/commoncap.c
security/device_cgroup.c
security/keys/encrypted-keys/encrypted.c
security/selinux/include/classmap.h
security/tomoyo/common.c
sound/core/hwdep.c
sound/core/memalloc.c
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/pci/hda/patch_realtek.c
sound/usb/mixer.c
sound/usb/mixer_maps.c
sound/usb/quirks-table.h
tools/arch/x86/include/asm/orc_types.h
tools/arch/x86/include/uapi/asm/unistd.h
tools/build/Makefile.feature
tools/build/feature/Makefile
tools/build/feature/test-libopencsd.c
tools/build/feature/test-libpfm4.c [new file with mode: 0644]
tools/include/linux/rbtree.h
tools/include/linux/rbtree_augmented.h
tools/include/uapi/linux/kvm.h
tools/include/uapi/linux/stat.h
tools/lib/api/fs/fs.c
tools/lib/api/fs/fs.h
tools/lib/api/io.h [new file with mode: 0644]
tools/lib/perf/cpumap.c
tools/lib/perf/evlist.c
tools/lib/subcmd/parse-options.h
tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h
tools/lib/traceevent/kbuffer-parse.c
tools/lib/traceevent/parse-filter.c
tools/objtool/Build
tools/objtool/Documentation/stack-validation.txt
tools/objtool/Makefile
tools/objtool/arch.h
tools/objtool/arch/x86/decode.c
tools/objtool/arch/x86/include/cfi_regs.h [new file with mode: 0644]
tools/objtool/builtin-check.c
tools/objtool/builtin-orc.c
tools/objtool/builtin.h
tools/objtool/cfi.h
tools/objtool/check.c
tools/objtool/check.h
tools/objtool/elf.c
tools/objtool/elf.h
tools/objtool/objtool.c
tools/objtool/objtool.h [new file with mode: 0644]
tools/objtool/orc.h [deleted file]
tools/objtool/orc_dump.c
tools/objtool/orc_gen.c
tools/objtool/weak.c [new file with mode: 0644]
tools/perf/Documentation/Makefile
tools/perf/Documentation/itrace.txt
tools/perf/Documentation/perf-bench.txt
tools/perf/Documentation/perf-c2c.txt
tools/perf/Documentation/perf-intel-pt.txt
tools/perf/Documentation/perf-list.txt
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-stat.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Documentation/perf.data-file-format.txt
tools/perf/Makefile.perf
tools/perf/arch/arm/util/cs-etm.c
tools/perf/arch/arm64/util/arm-spe.c
tools/perf/arch/powerpc/util/header.c
tools/perf/arch/powerpc/util/kvm-stat.c
tools/perf/arch/s390/util/kvm-stat.c
tools/perf/arch/x86/tests/perf-time-to-tsc.c
tools/perf/arch/x86/util/intel-bts.c
tools/perf/arch/x86/util/intel-pt.c
tools/perf/arch/x86/util/kvm-stat.c
tools/perf/bench/Build
tools/perf/bench/bench.h
tools/perf/bench/epoll-wait.c
tools/perf/bench/futex-hash.c
tools/perf/bench/futex-lock-pi.c
tools/perf/bench/kallsyms-parse.c [new file with mode: 0644]
tools/perf/bench/synthesize.c [new file with mode: 0644]
tools/perf/builtin-annotate.c
tools/perf/builtin-bench.c
tools/perf/builtin-c2c.c
tools/perf/builtin-diff.c
tools/perf/builtin-ftrace.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-kvm.c
tools/perf/builtin-lock.c
tools/perf/builtin-mem.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-timechart.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/design.txt
tools/perf/pmu-events/arch/powerpc/power9/nest_metrics.json [new file with mode: 0644]
tools/perf/pmu-events/pmu-events.h
tools/perf/scripts/python/bin/flamegraph-record [new file with mode: 0755]
tools/perf/scripts/python/bin/flamegraph-report [new file with mode: 0755]
tools/perf/scripts/python/flamegraph.py [new file with mode: 0755]
tools/perf/tests/Build
tools/perf/tests/api-io.c [new file with mode: 0644]
tools/perf/tests/builtin-test.c
tools/perf/tests/event-times.c
tools/perf/tests/event_update.c
tools/perf/tests/evsel-roundtrip-name.c
tools/perf/tests/evsel-tp-sched.c
tools/perf/tests/expr.c
tools/perf/tests/hists_cumulate.c
tools/perf/tests/mmap-basic.c
tools/perf/tests/openat-syscall-all-cpus.c
tools/perf/tests/openat-syscall-tp-fields.c
tools/perf/tests/openat-syscall.c
tools/perf/tests/parse-events.c
tools/perf/tests/perf-record.c
tools/perf/tests/sample-parsing.c
tools/perf/tests/switch-tracking.c
tools/perf/tests/tests.h
tools/perf/tests/topology.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/gtk/annotate.c
tools/perf/ui/gtk/hists.c
tools/perf/ui/hist.c
tools/perf/util/Build
tools/perf/util/annotate.c
tools/perf/util/arm-spe.c
tools/perf/util/auxtrace.c
tools/perf/util/auxtrace.h
tools/perf/util/bpf-event.c
tools/perf/util/bpf-event.h
tools/perf/util/bpf-loader.c
tools/perf/util/branch.h
tools/perf/util/callchain.h
tools/perf/util/cap.h
tools/perf/util/cgroup.c
tools/perf/util/cloexec.c
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
tools/perf/util/cs-etm.c
tools/perf/util/cs-etm.h
tools/perf/util/data-convert-bt.c
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/env.h
tools/perf/util/event.c
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/evsel_config.h
tools/perf/util/evsel_fprintf.c
tools/perf/util/expr.c
tools/perf/util/expr.h
tools/perf/util/expr.l
tools/perf/util/expr.y
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/hist.c
tools/perf/util/intel-bts.c
tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
tools/perf/util/intel-pt.c
tools/perf/util/machine.c
tools/perf/util/mem2node.c
tools/perf/util/metricgroup.c
tools/perf/util/metricgroup.h
tools/perf/util/ordered-events.c
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l
tools/perf/util/parse-events.y
tools/perf/util/perf_api_probe.c [new file with mode: 0644]
tools/perf/util/perf_api_probe.h [new file with mode: 0644]
tools/perf/util/pmu.c
tools/perf/util/pmu.h
tools/perf/util/python.c
tools/perf/util/record.c
tools/perf/util/record.h
tools/perf/util/s390-cpumcf-kernel.h
tools/perf/util/s390-cpumsf.c
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/session.c
tools/perf/util/sideband_evlist.c [new file with mode: 0644]
tools/perf/util/smt.c
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/stat-display.c
tools/perf/util/stat-shadow.c
tools/perf/util/stat.c
tools/perf/util/symbol.c
tools/perf/util/synthetic-events.c
tools/perf/util/thread-stack.c
tools/perf/util/thread-stack.h
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/top.c
tools/perf/util/top.h
tools/perf/util/trace-event-read.c
tools/perf/util/util.c
tools/power/cpupower/utils/cpupower-info.c
tools/power/cpupower/utils/cpupower-set.c
tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c
tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c
tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c
tools/power/cpupower/utils/idle_monitor/nhm_idle.c
tools/power/cpupower/utils/idle_monitor/snb_idle.c
tools/power/pm-graph/config/custom-timeline-functions.cfg
tools/power/pm-graph/sleepgraph.py
tools/power/x86/intel-speed-select/isst-config.c
tools/power/x86/intel-speed-select/isst-core.c
tools/power/x86/intel-speed-select/isst-display.c
tools/power/x86/intel-speed-select/isst.h
tools/spi/Makefile
tools/spi/spidev_test.c
tools/testing/selftests/bpf/verifier/bounds.c
tools/testing/selftests/pstore/pstore_tests
tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh [new file with mode: 0755]
tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh
tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
tools/testing/selftests/rcutorture/bin/kvm.sh
tools/testing/selftests/rcutorture/configs/rcu/CFLIST
tools/testing/selftests/rcutorture/configs/rcu/RUDE01 [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/RUDE01.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TRACE01 [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TRACE01.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TRACE02 [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TRACE02.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TREE10
tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json [new file with mode: 0644]
tools/testing/selftests/wireguard/qemu/debug.config
virt/kvm/arm/arm.c
virt/kvm/arm/vgic/vgic-mmio-v3.c
virt/kvm/arm/vgic/vgic.h

index 4f906b4e9785f7628ff5fe79fd4da3654ffc7bdc..c69d9c734fb5e7e2268870ed680e34a0b29b9bf3 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -152,6 +152,7 @@ Krzysztof Kozlowski <krzk@kernel.org> <k.kozlowski.k@gmail.com>
 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 Leon Romanovsky <leon@kernel.org> <leon@leon.nu>
 Leon Romanovsky <leon@kernel.org> <leonro@mellanox.com>
+Leonardo Bras <leobras.c@gmail.com> <leonardo@linux.ibm.com>
 Leonid I Ananiev <leonid.i.ananiev@intel.com>
 Linas Vepstas <linas@austin.ibm.com>
 Linus Lüssing <linus.luessing@c0d3.blue> <linus.luessing@web.de>
@@ -234,7 +235,9 @@ Ralf Baechle <ralf@linux-mips.org>
 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
 Randy Dunlap <rdunlap@infradead.org> <rdunlap@xenotime.net>
 Rémi Denis-Courmont <rdenis@simphalempin.com>
-Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
+Ricardo Ribalda <ribalda@kernel.org> <ricardo.ribalda@gmail.com>
+Ricardo Ribalda <ribalda@kernel.org> <ricardo@ribalda.com>
+Ricardo Ribalda <ribalda@kernel.org> Ricardo Ribalda Delgado <ribalda@kernel.org>
 Ross Zwisler <zwisler@kernel.org> <ross.zwisler@linux.intel.com>
 Rudolf Marek <R.Marek@sh.cvut.cz>
 Rui Saraiva <rmps@joel.ist.utl.pt>
diff --git a/CREDITS b/CREDITS
index 032b5994f4760a13770d1629b8b057195d9266c2..0787b5872906c8a92a63cde3961ed630e2ec93b6 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -3104,14 +3104,16 @@ W: http://www.qsl.net/dl1bke/
 D: Generic Z8530 driver, AX.25 DAMA slave implementation
 D: Several AX.25 hacks
 
-N: Ricardo Ribalda Delgado
-E: ricardo.ribalda@gmail.com
+N: Ricardo Ribalda
+E: ribalda@kernel.org
 W: http://ribalda.com
 D: PLX USB338x driver
 D: PCA9634 driver
 D: Option GTM671WFS
 D: Fintek F81216A
 D: AD5761 iio driver
+D: TI DAC7612 driver
+D: Sony IMX214 driver
 D: Various kernel hacks
 S: Qtechnology A/S
 S: Valby Langgade 142
diff --git a/Documentation/ABI/obsolete/sysfs-cpuidle b/Documentation/ABI/obsolete/sysfs-cpuidle
new file mode 100644 (file)
index 0000000..e398fb5
--- /dev/null
@@ -0,0 +1,9 @@
+What:          /sys/devices/system/cpu/cpuidle/current_governor_ro
+Date:          April, 2020
+Contact:       linux-pm@vger.kernel.org
+Description:
+       current_governor_ro shows current using cpuidle governor, but read only.
+       with the update that cpuidle governor can be changed at runtime in default,
+       both current_governor and current_governor_ro co-exist under
+       /sys/devices/system/cpu/cpuidle/ file, it's duplicate so make
+       current_governor_ro obselete.
diff --git a/Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt b/Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
new file mode 100644 (file)
index 0000000..39d5659
--- /dev/null
@@ -0,0 +1,22 @@
+These files allow sending arbitrary IPC commands to the PMC/SCU which
+may be dangerous. These will be removed eventually and should not be
+used in any new applications.
+
+What:          /sys/bus/platform/devices/INT34D2:00/simplecmd
+Date:          Jun 2015
+KernelVersion: 4.1
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This interface allows userspace to send an arbitrary
+               IPC command to the PMC/SCU.
+
+               Format: %d %d where first number is command and
+               second number is subcommand.
+
+What:          /sys/bus/platform/devices/INT34D2:00/northpeak
+Date:          Jun 2015
+KernelVersion: 4.1
+Contact:       Mika Westerberg <mika.westerberg@linux.intel.com>
+Description:   This interface allows userspace to enable and disable
+               Northpeak through the PMC/SCU.
+
+               Format: %u.
index df8413cf1468c9dbab35693d3fa524469a30061c..484fc04bcc2547b1f871dcc36a8ad0e60404cc50 100644 (file)
@@ -54,7 +54,7 @@ Date:         October 2002
 Contact:       Linux Memory Management list <linux-mm@kvack.org>
 Description:
                Provides information about the node's distribution and memory
-               utilization. Similar to /proc/meminfo, see Documentation/filesystems/proc.txt
+               utilization. Similar to /proc/meminfo, see Documentation/filesystems/proc.rst
 
 What:          /sys/devices/system/node/nodeX/numastat
 Date:          October 2002
index ec4a79e3a807363997c14213fe5b502c49cc9f20..b4be5f1db4b77dd629d9d5c6d531d4e1a93bd45d 100644 (file)
@@ -33,7 +33,7 @@ Contact:        linux-crypto@vger.kernel.org
 Description:    Dump debug registers from the HPRE.
                Only available for PF.
 
-What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/qm_regs
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/regs
 Date:           Sep 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    Dump debug registers from the QM.
@@ -44,14 +44,97 @@ What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/current_q
 Date:           Sep 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    One QM may contain multiple queues. Select specific queue to
-               show its debug registers in above qm_regs.
+               show its debug registers in above regs.
                Only available for PF.
 
 What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/clear_enable
 Date:           Sep 2019
 Contact:        linux-crypto@vger.kernel.org
-Description:    QM debug registers(qm_regs) read clear control. 1 means enable
+Description:    QM debug registers(regs) read clear control. 1 means enable
                register read clear, otherwise 0.
                Writing to this file has no functional effect, only enable or
                disable counters clear after reading of these registers.
                Only available for PF.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/err_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of invalid interrupts for
+               QM task completion.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/aeq_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of QM async event queue interrupts.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/abnormal_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of interrupts for QM abnormal event.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/create_qp_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of queue allocation errors.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/mb_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of failed QM mailbox commands.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/qm/status
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the status of the QM.
+               Four states: initiated, started, stopped and closed.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/send_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of sent requests.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/recv_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of received requests.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/send_busy_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of requests sent
+               with returning busy.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/send_fail_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of completed but error requests.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/invalid_req_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of invalid requests being received.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/overtime_thrhld
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Set the threshold time for counting the request which is
+               processed longer than the threshold.
+               0: disable(default), 1: 1 microsecond.
+               Available for both PF and VF, and take no other effect on HPRE.
+
+What:           /sys/kernel/debug/hisi_hpre/<bdf>/hpre_dfx/over_thrhld_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of time out requests.
+               Available for both PF and VF, and take no other effect on HPRE.
index 06adb899495e0efaac3512be154f17ead4b923ae..85feb4408e0f63b1800fcce045183d2abfe00a2e 100644 (file)
@@ -1,10 +1,4 @@
-What:           /sys/kernel/debug/hisi_sec/<bdf>/sec_dfx
-Date:           Oct 2019
-Contact:        linux-crypto@vger.kernel.org
-Description:    Dump the debug registers of SEC cores.
-               Only available for PF.
-
-What:           /sys/kernel/debug/hisi_sec/<bdf>/clear_enable
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/clear_enable
 Date:           Oct 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    Enabling/disabling of clear action after reading
@@ -12,7 +6,7 @@ Description:    Enabling/disabling of clear action after reading
                0: disable, 1: enable.
                Only available for PF, and take no other effect on SEC.
 
-What:           /sys/kernel/debug/hisi_sec/<bdf>/current_qm
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/current_qm
 Date:           Oct 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    One SEC controller has one PF and multiple VFs, each function
@@ -20,24 +14,100 @@ Description:    One SEC controller has one PF and multiple VFs, each function
                qm refers to.
                Only available for PF.
 
-What:           /sys/kernel/debug/hisi_sec/<bdf>/qm/qm_regs
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/qm_regs
 Date:           Oct 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    Dump of QM related debug registers.
                Available for PF and VF in host. VF in guest currently only
                has one debug register.
 
-What:           /sys/kernel/debug/hisi_sec/<bdf>/qm/current_q
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/current_q
 Date:           Oct 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    One QM of SEC may contain multiple queues. Select specific
-               queue to show its debug registers in above 'qm_regs'.
+               queue to show its debug registers in above 'regs'.
                Only available for PF.
 
-What:           /sys/kernel/debug/hisi_sec/<bdf>/qm/clear_enable
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/clear_enable
 Date:           Oct 2019
 Contact:        linux-crypto@vger.kernel.org
 Description:    Enabling/disabling of clear action after reading
                the SEC's QM debug registers.
                0: disable, 1: enable.
                Only available for PF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/err_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of invalid interrupts for
+               QM task completion.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/aeq_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of QM async event queue interrupts.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/abnormal_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of interrupts for QM abnormal event.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/create_qp_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of queue allocation errors.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/mb_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of failed QM mailbox commands.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/qm/status
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the status of the QM.
+               Four states: initiated, started, stopped and closed.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/send_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of sent requests.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/recv_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of received requests.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/send_busy_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of requests sent with returning busy.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/err_bd_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of BD type error requests
+               to be received.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/invalid_req_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of invalid requests being received.
+               Available for both PF and VF, and take no other effect on SEC.
+
+What:           /sys/kernel/debug/hisi_sec2/<bdf>/sec_dfx/done_flag_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of completed but marked error requests
+               to be received.
+               Available for both PF and VF, and take no other effect on SEC.
index a7c63e6c4bc3b96adc29f38bbc856359d0cae3a6..3034a2bf99ca5b94dac5951da2d9468ac6a176e7 100644 (file)
@@ -26,7 +26,7 @@ Description:    One ZIP controller has one PF and multiple VFs, each function
                has a QM. Select the QM which below qm refers to.
                Only available for PF.
 
-What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/qm_regs
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/regs
 Date:           Nov 2018
 Contact:        linux-crypto@vger.kernel.org
 Description:    Dump of QM related debug registers.
@@ -37,14 +37,78 @@ What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/current_q
 Date:           Nov 2018
 Contact:        linux-crypto@vger.kernel.org
 Description:    One QM may contain multiple queues. Select specific queue to
-               show its debug registers in above qm_regs.
+               show its debug registers in above regs.
                Only available for PF.
 
 What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/clear_enable
 Date:           Nov 2018
 Contact:        linux-crypto@vger.kernel.org
-Description:    QM debug registers(qm_regs) read clear control. 1 means enable
+Description:    QM debug registers(regs) read clear control. 1 means enable
                register read clear, otherwise 0.
                Writing to this file has no functional effect, only enable or
                disable counters clear after reading of these registers.
                Only available for PF.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/err_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of invalid interrupts for
+               QM task completion.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/aeq_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of QM async event queue interrupts.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/abnormal_irq
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of interrupts for QM abnormal event.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/create_qp_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of queue allocation errors.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/mb_err
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the number of failed QM mailbox commands.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/qm/status
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the status of the QM.
+               Four states: initiated, started, stopped and closed.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/zip_dfx/send_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of sent requests.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/zip_dfx/recv_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of received requests.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/zip_dfx/send_busy_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of requests received
+               with returning busy.
+               Available for both PF and VF, and take no other effect on ZIP.
+
+What:           /sys/kernel/debug/hisi_zip/<bdf>/zip_dfx/err_bd_cnt
+Date:           Apr 2020
+Contact:        linux-crypto@vger.kernel.org
+Description:    Dump the total number of BD type error requests
+               to be received.
+               Available for both PF and VF, and take no other effect on ZIP.
index f307506eb54ce9a44601ba37ba798314049da762..1e6c28b1942bd45de652339b807cff538140dd62 100644 (file)
@@ -56,6 +56,11 @@ Description: The /dev/kmsg character device node provides userspace access
                  seek after the last record available at the time
                  the last SYSLOG_ACTION_CLEAR was issued.
 
+               Due to the record nature of this interface with a "read all"
+               behavior and the specific positions each seek operation sets,
+               SEEK_CUR is not supported, returning -ESPIPE (invalid seek) to
+               errno whenever requested.
+
                The output format consists of a prefix carrying the syslog
                prefix including priority and facility, the 64 bit message
                sequence number and the monotonic timestamp in microseconds,
index 274df44d8b1b50ae7fea4c0b81e0eeac61d8826e..0469781933684cb8bb6e5809e2372a6af6c2e98f 100644 (file)
@@ -11,7 +11,7 @@ Description:
                Additionally, the fields Pss_Anon, Pss_File and Pss_Shmem
                are not present in /proc/pid/smaps.  These fields represent
                the sum of the Pss field of each type (anon, file, shmem).
-               For more details, see Documentation/filesystems/proc.txt
+               For more details, see Documentation/filesystems/proc.rst
                and the procfs man page.
 
                Typical output looks like this:
index 2e0e3b45d02a7f7bd428d45de2f37265d9055aed..6b5dafab950ca2bef595deedf0ddd4e11c841e1d 100644 (file)
@@ -106,10 +106,10 @@ Description:      CPU topology files that describe a logical CPU's relationship
                See Documentation/admin-guide/cputopology.rst for more information.
 
 
-What:          /sys/devices/system/cpu/cpuidle/current_driver
-               /sys/devices/system/cpu/cpuidle/current_governer_ro
-               /sys/devices/system/cpu/cpuidle/available_governors
+What:          /sys/devices/system/cpu/cpuidle/available_governors
+               /sys/devices/system/cpu/cpuidle/current_driver
                /sys/devices/system/cpu/cpuidle/current_governor
+               /sys/devices/system/cpu/cpuidle/current_governer_ro
 Date:          September 2007
 Contact:       Linux kernel mailing list <linux-kernel@vger.kernel.org>
 Description:   Discover cpuidle policy and mechanism
@@ -119,24 +119,18 @@ Description:      Discover cpuidle policy and mechanism
                consumption during idle.
 
                Idle policy (governor) is differentiated from idle mechanism
-               (driver)
-
-               current_driver: (RO) displays current idle mechanism
-
-               current_governor_ro: (RO) displays current idle policy
-
-               With the cpuidle_sysfs_switch boot option enabled (meant for
-               developer testing), the following three attributes are visible
-               instead:
-
-               current_driver: same as described above
+               (driver).
 
                available_governors: (RO) displays a space separated list of
-               available governors
+               available governors.
+
+               current_driver: (RO) displays current idle mechanism.
 
                current_governor: (RW) displays current idle policy. Users can
                switch the governor at runtime by writing to this file.
 
+               current_governor_ro: (RO) displays current idle policy.
+
                See Documentation/admin-guide/pm/cpuidle.rst and
                Documentation/driver-api/pm/cpuidle.rst for more information.
 
diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update b/Documentation/ABI/testing/sysfs-platform-intel-wmi-sbl-fw-update
new file mode 100644 (file)
index 0000000..5aa6189
--- /dev/null
@@ -0,0 +1,12 @@
+What:          /sys/bus/wmi/devices/44FADEB1-B204-40F2-8581-394BBDC1B651/firmware_update_request
+Date:          April 2020
+KernelVersion: 5.7
+Contact:       "Jithu Joseph" <jithu.joseph@intel.com>
+Description:
+               Allow user space entities to trigger update of Slim
+               Bootloader (SBL). This attribute normally has a value
+               of 0 and userspace can signal SBL to update firmware,
+               on next reboot, by writing a value of 1.
+               There are two available states:
+                   * 0 -> Skip firmware update while rebooting
+                   * 1 -> Attempt firmware update on next reboot
index cc786d11a02829e1ba1e078f302021f21f992b30..db1fc35ded50b9d98a97072559e84f08682ba42b 100644 (file)
@@ -98,7 +98,11 @@ else # HAVE_PDFLATEX
 
 pdfdocs: latexdocs
        @$(srctree)/scripts/sphinx-pre-install --version-check
-       $(foreach var,$(SPHINXDIRS), $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" -C $(BUILDDIR)/$(var)/latex || exit;)
+       $(foreach var,$(SPHINXDIRS), \
+          $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" -C $(BUILDDIR)/$(var)/latex || exit; \
+          mkdir -p $(BUILDDIR)/$(var)/pdf; \
+          mv $(subst .tex,.pdf,$(wildcard $(BUILDDIR)/$(var)/latex/*.tex)) $(BUILDDIR)/$(var)/pdf/; \
+       )
 
 endif # HAVE_PDFLATEX
 
index d078ef3eb19209acaab81725423192c19ef5426c..2ec70121bfca5f265c0377cb0284350913dfa251 100644 (file)
@@ -32,12 +32,13 @@ interrupt goes unhandled over time, they are tracked by the Linux kernel as
 Spurious Interrupts. The IRQ will be disabled by the Linux kernel after it
 reaches a specific count with the error "nobody cared". This disabled IRQ
 now prevents valid usage by an existing interrupt which may happen to share
-the IRQ line.
+the IRQ line::
 
   irq 19: nobody cared (try booting with the "irqpoll" option)
   CPU: 0 PID: 2988 Comm: irq/34-nipalk Tainted: 4.14.87-rt49-02410-g4a640ec-dirty #1
   Hardware name: National Instruments NI PXIe-8880/NI PXIe-8880, BIOS 2.1.5f1 01/09/2020
   Call Trace:
+
   <IRQ>
    ? dump_stack+0x46/0x5e
    ? __report_bad_irq+0x2e/0xb0
@@ -85,15 +86,18 @@ Mitigations
 The mitigations take the form of PCI quirks. The preference has been to
 first identify and make use of a means to disable the routing to the PCH.
 In such a case a quirk to disable boot interrupt generation can be
-added.[1]
+added. [1]_
 
-  Intel® 6300ESB I/O Controller Hub
+Intel® 6300ESB I/O Controller Hub
   Alternate Base Address Register:
    BIE: Boot Interrupt Enable
-         0 = Boot interrupt is enabled.
-         1 = Boot interrupt is disabled.
 
-  Intel® Sandy Bridge through Sky Lake based Xeon servers:
+         ==  ===========================
+         0   Boot interrupt is enabled.
+         1   Boot interrupt is disabled.
+         ==  ===========================
+
+Intel® Sandy Bridge through Sky Lake based Xeon servers:
   Coherent Interface Protocol Interrupt Control
    dis_intx_route2pch/dis_intx_route2ich/dis_intx_route2dmi2:
          When this bit is set. Local INTx messages received from the
@@ -109,12 +113,12 @@ line by default.  Therefore, on chipsets where this INTx routing cannot be
 disabled, the Linux kernel will reroute the valid interrupt to its legacy
 interrupt. This redirection of the handler will prevent the occurrence of
 the spurious interrupt detection which would ordinarily disable the IRQ
-line due to excessive unhandled counts.[2]
+line due to excessive unhandled counts. [2]_
 
 The config option X86_REROUTE_FOR_BROKEN_BOOT_IRQS exists to enable (or
 disable) the redirection of the interrupt handler to the PCH interrupt
 line. The option can be overridden by either pci=ioapicreroute or
-pci=noioapicreroute.[3]
+pci=noioapicreroute. [3]_
 
 
 More Documentation
@@ -127,19 +131,19 @@ into the evolution of its handling with chipsets.
 Example of disabling of the boot interrupt
 ------------------------------------------
 
-Intel® 6300ESB I/O Controller Hub (Document # 300641-004US)
+      - Intel® 6300ESB I/O Controller Hub (Document # 300641-004US)
        5.7.3 Boot Interrupt
        https://www.intel.com/content/dam/doc/datasheet/6300esb-io-controller-hub-datasheet.pdf
 
-Intel® Xeon® Processor E5-1600/2400/2600/4600 v3 Product Families
-Datasheet - Volume 2: Registers (Document # 330784-003)
+      - Intel® Xeon® Processor E5-1600/2400/2600/4600 v3 Product Families
+       Datasheet - Volume 2: Registers (Document # 330784-003)
        6.6.41 cipintrc Coherent Interface Protocol Interrupt Control
        https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/xeon-e5-v3-datasheet-vol-2.pdf
 
 Example of handler rerouting
 ----------------------------
 
-Intel® 6700PXH 64-bit PCI Hub (Document # 302628)
+      - Intel® 6700PXH 64-bit PCI Hub (Document # 302628)
        2.15.2 PCI Express Legacy INTx Support and Boot Interrupt
        https://www.intel.com/content/dam/doc/datasheet/6700pxh-64-bit-pci-hub-datasheet.pdf
 
@@ -150,6 +154,6 @@ Cheers,
     Sean V Kelley
     sean.v.kelley@linux.intel.com
 
-[1] https://lore.kernel.org/r/12131949181903-git-send-email-sassmann@suse.de/
-[2] https://lore.kernel.org/r/12131949182094-git-send-email-sassmann@suse.de/
-[3] https://lore.kernel.org/r/487C8EA7.6020205@suse.de/
+.. [1] https://lore.kernel.org/r/12131949181903-git-send-email-sassmann@suse.de/
+.. [2] https://lore.kernel.org/r/12131949182094-git-send-email-sassmann@suse.de/
+.. [3] https://lore.kernel.org/r/487C8EA7.6020205@suse.de/
index fd5e2cbc4935d64741a60095a93ecc9143181c6a..75b8ca007a11c4c9a06b4d579cd704e25eed7535 100644 (file)
@@ -1943,56 +1943,27 @@ invoked from a CPU-hotplug notifier.
 Scheduler and RCU
 ~~~~~~~~~~~~~~~~~
 
-RCU depends on the scheduler, and the scheduler uses RCU to protect some
-of its data structures. The preemptible-RCU ``rcu_read_unlock()``
-implementation must therefore be written carefully to avoid deadlocks
-involving the scheduler's runqueue and priority-inheritance locks. In
-particular, ``rcu_read_unlock()`` must tolerate an interrupt where the
-interrupt handler invokes both ``rcu_read_lock()`` and
-``rcu_read_unlock()``. This possibility requires ``rcu_read_unlock()``
-to use negative nesting levels to avoid destructive recursion via
-interrupt handler's use of RCU.
-
-This scheduler-RCU requirement came as a `complete
-surprise <https://lwn.net/Articles/453002/>`__.
-
-As noted above, RCU makes use of kthreads, and it is necessary to avoid
-excessive CPU-time accumulation by these kthreads. This requirement was
-no surprise, but RCU's violation of it when running context-switch-heavy
-workloads when built with ``CONFIG_NO_HZ_FULL=y`` `did come as a
-surprise
+RCU makes use of kthreads, and it is necessary to avoid excessive CPU-time
+accumulation by these kthreads. This requirement was no surprise, but
+RCU's violation of it when running context-switch-heavy workloads when
+built with ``CONFIG_NO_HZ_FULL=y`` `did come as a surprise
 [PDF] <http://www.rdrop.com/users/paulmck/scalability/paper/BareMetal.2015.01.15b.pdf>`__.
 RCU has made good progress towards meeting this requirement, even for
 context-switch-heavy ``CONFIG_NO_HZ_FULL=y`` workloads, but there is
 room for further improvement.
 
-It is forbidden to hold any of scheduler's runqueue or
-priority-inheritance spinlocks across an ``rcu_read_unlock()`` unless
-interrupts have been disabled across the entire RCU read-side critical
-section, that is, up to and including the matching ``rcu_read_lock()``.
-Violating this restriction can result in deadlocks involving these
-scheduler spinlocks. There was hope that this restriction might be
-lifted when interrupt-disabled calls to ``rcu_read_unlock()`` started
-deferring the reporting of the resulting RCU-preempt quiescent state
-until the end of the corresponding interrupts-disabled region.
-Unfortunately, timely reporting of the corresponding quiescent state to
-expedited grace periods requires a call to ``raise_softirq()``, which
-can acquire these scheduler spinlocks. In addition, real-time systems
-using RCU priority boosting need this restriction to remain in effect
-because deferred quiescent-state reporting would also defer deboosting,
-which in turn would degrade real-time latencies.
-
-In theory, if a given RCU read-side critical section could be guaranteed
-to be less than one second in duration, holding a scheduler spinlock
-across that critical section's ``rcu_read_unlock()`` would require only
-that preemption be disabled across the entire RCU read-side critical
-section, not interrupts. Unfortunately, given the possibility of vCPU
-preemption, long-running interrupts, and so on, it is not possible in
-practice to guarantee that a given RCU read-side critical section will
-complete in less than one second. Therefore, as noted above, if
-scheduler spinlocks are held across a given call to
-``rcu_read_unlock()``, interrupts must be disabled across the entire RCU
-read-side critical section.
+There is no longer any prohibition against holding any of
+scheduler's runqueue or priority-inheritance spinlocks across an
+``rcu_read_unlock()``, even if interrupts and preemption were enabled
+somewhere within the corresponding RCU read-side critical section.
+Therefore, it is now perfectly legal to execute ``rcu_read_lock()``
+with preemption enabled, acquire one of the scheduler locks, and hold
+that lock across the matching ``rcu_read_unlock()``.
+
+Similarly, the RCU flavor consolidation has removed the need for negative
+nesting.  The fact that interrupt-disabled regions of code act as RCU
+read-side critical sections implicitly avoids earlier issues that used
+to result in destructive recursion via interrupt handler's use of RCU.
 
 Tracing and RCU
 ~~~~~~~~~~~~~~~
index da37455f96c9b2490e43d38b0982f821cce91443..5d7e25988085b527e6668e207c4c5c6eb2995b84 100644 (file)
@@ -63,7 +63,7 @@ which can then be compiled to AML binary format::
     ASL Input:     minnomax.asl - 30 lines, 614 bytes, 7 keywords
     AML Output:    minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes
 
-[1] http://wiki.minnowboard.org/MinnowBoard_MAX#Low_Speed_Expansion_Connector_.28Top.29
+[1] https://www.elinux.org/Minnowboard:MinnowMax#Low_Speed_Expansion_.28Top.29
 
 The resulting AML code can then be loaded by the kernel using one of the methods
 below.
index 44b8a4edd3484a586998a992b7a301659ba7803e..f7c80f4649fcb0c45fbd9cc12da2d2868f81314f 100644 (file)
@@ -49,15 +49,19 @@ the issue, it may also contain the word **Oops**, as on this one::
 
 Despite being an **Oops** or some other sort of stack trace, the offended
 line is usually required to identify and handle the bug. Along this chapter,
-we'll refer to "Oops" for all kinds of stack traces that need to be analized.
+we'll refer to "Oops" for all kinds of stack traces that need to be analyzed.
 
-.. note::
+If the kernel is compiled with ``CONFIG_DEBUG_INFO``, you can enhance the
+quality of the stack trace by using file:`scripts/decode_stacktrace.sh`.
+
+Modules linked in
+-----------------
+
+Modules that are tainted or are being loaded or unloaded are marked with
+"(...)", where the taint flags are described in
+file:`Documentation/admin-guide/tainted-kernels.rst`, "being loaded" is
+annotated with "+", and "being unloaded" is annotated with "-".
 
-  ``ksymoops`` is useless on 2.6 or upper.  Please use the Oops in its original
-  format (from ``dmesg``, etc).  Ignore any references in this or other docs to
-  "decoding the Oops" or "running it through ksymoops".
-  If you post an Oops from 2.6+ that has been run through ``ksymoops``,
-  people will just tell you to repost it.
 
 Where is the Oops message is located?
 -------------------------------------
@@ -71,7 +75,7 @@ by running ``journalctl`` command.
 Sometimes ``klogd`` dies, in which case you can run ``dmesg > file`` to
 read the data from the kernel buffers and save it.  Or you can
 ``cat /proc/kmsg > file``, however you have to break in to stop the transfer,
-``kmsg`` is a "never ending file".
+since ``kmsg`` is a "never ending file".
 
 If the machine has crashed so badly that you cannot enter commands or
 the disk is not available then you have three options:
@@ -81,9 +85,9 @@ the disk is not available then you have three options:
     planned for a crash. Alternatively, you can take a picture of
     the screen with a digital camera - not nice, but better than
     nothing.  If the messages scroll off the top of the console, you
-    may find that booting with a higher resolution (eg, ``vga=791``)
+    may find that booting with a higher resolution (e.g., ``vga=791``)
     will allow you to read more of the text. (Caveat: This needs ``vesafb``,
-    so won't help for 'early' oopses)
+    so won't help for 'early' oopses.)
 
 (2) Boot with a serial console (see
     :ref:`Documentation/admin-guide/serial-console.rst <serial_console>`),
@@ -104,7 +108,7 @@ Kernel source file. There are two methods for doing that. Usually, using
 gdb
 ^^^
 
-The GNU debug (``gdb``) is the best way to figure out the exact file and line
+The GNU debugger (``gdb``) is the best way to figure out the exact file and line
 number of the OOPS from the ``vmlinux`` file.
 
 The usage of gdb works best on a kernel compiled with ``CONFIG_DEBUG_INFO``.
@@ -165,7 +169,7 @@ If you have a call trace, such as::
       [<ffffffff8802770b>] :jbd:journal_stop+0x1be/0x1ee
       ...
 
-this shows the problem likely in the :jbd: module. You can load that module
+this shows the problem likely is in the :jbd: module. You can load that module
 in gdb and list the relevant code::
 
   $ gdb fs/jbd/jbd.ko
@@ -199,8 +203,9 @@ in the kernel hacking menu of the menu configuration.) For example::
    You need to be at the top level of the kernel tree for this to pick up
    your C files.
 
-If you don't have access to the code you can also debug on some crash dumps
-e.g. crash dump output as shown by Dave Miller::
+If you don't have access to the source code you can still debug some crash
+dumps using the following method (example crash dump output as shown by
+Dave Miller)::
 
      EIP is at         +0x14/0x4c0
       ...
@@ -230,6 +235,9 @@ e.g. crash dump output as shown by Dave Miller::
          mov        0x8(%ebp), %ebx         ! %ebx = skb->sk
          mov        0x13c(%ebx), %eax       ! %eax = inet_sk(sk)->opt
 
+file:`scripts/decodecode` can be used to automate most of this, depending
+on what CPU architecture is being debugged.
+
 Reporting the bug
 -----------------
 
@@ -241,7 +249,7 @@ used for the development of the affected code. This can be done by using
 the ``get_maintainer.pl`` script.
 
 For example, if you find a bug at the gspca's sonixj.c file, you can get
-their maintainers with::
+its maintainers with::
 
        $ ./scripts/get_maintainer.pl -f drivers/media/usb/gspca/sonixj.c
        Hans Verkuil <hverkuil@xs4all.nl> (odd fixer:GSPCA USB WEBCAM DRIVER,commit_signer:1/1=100%)
@@ -253,16 +261,17 @@ their maintainers with::
 
 Please notice that it will point to:
 
-- The last developers that touched on the source code. On the above example,
-  Tejun and Bhaktipriya (in this specific case, none really envolved on the
-  development of this file);
+- The last developers that touched the source code (if this is done inside
+  a git tree). On the above example, Tejun and Bhaktipriya (in this
+  specific case, none really envolved on the development of this file);
 - The driver maintainer (Hans Verkuil);
 - The subsystem maintainer (Mauro Carvalho Chehab);
 - The driver and/or subsystem mailing list (linux-media@vger.kernel.org);
 - the Linux Kernel mailing list (linux-kernel@vger.kernel.org).
 
 Usually, the fastest way to have your bug fixed is to report it to mailing
-list used for the development of the code (linux-media ML) copying the driver maintainer (Hans).
+list used for the development of the code (linux-media ML) copying the
+driver maintainer (Hans).
 
 If you are totally stumped as to whom to send the report, and
 ``get_maintainer.pl`` didn't provide you anything useful, send it to
@@ -303,9 +312,9 @@ protection fault message can be simply cut out of the message files
 and forwarded to the kernel developers.
 
 Two types of address resolution are performed by ``klogd``.  The first is
-static translation and the second is dynamic translation.  Static
-translation uses the System.map file in much the same manner that
-ksymoops does.  In order to do static translation the ``klogd`` daemon
+static translation and the second is dynamic translation.
+Static translation uses the System.map file.
+In order to do static translation the ``klogd`` daemon
 must be able to find a system map file at daemon initialization time.
 See the klogd man page for information on how ``klogd`` searches for map
 files.
index bcc80269bb6ac6cc2492b128fd5e167254350737..b8c0460730f34dcc3715432521eea98145d0d3bd 100644 (file)
@@ -1329,6 +1329,10 @@ PAGE_SIZE multiple when read back.
          workingset_activate
                Number of refaulted pages that were immediately activated
 
+         workingset_restore
+               Number of restored pages which have been detected as an active
+               workingset before they got reclaimed.
+
          workingset_nodereclaim
                Number of times a shadow node has been reclaimed
 
@@ -1370,6 +1374,22 @@ PAGE_SIZE multiple when read back.
        The total amount of swap currently being used by the cgroup
        and its descendants.
 
+  memory.swap.high
+       A read-write single value file which exists on non-root
+       cgroups.  The default is "max".
+
+       Swap usage throttle limit.  If a cgroup's swap usage exceeds
+       this limit, all its further allocations will be throttled to
+       allow userspace to implement custom out-of-memory procedures.
+
+       This limit marks a point of no return for the cgroup. It is NOT
+       designed to manage the amount of swapping a workload does
+       during regular operation. Compare to memory.swap.max, which
+       prohibits swapping past a set amount, but lets the cgroup
+       continue unimpeded as long as other memory can be reclaimed.
+
+       Healthy workloads are not expected to reach this limit.
+
   memory.swap.max
        A read-write single value file which exists on non-root
        cgroups.  The default is "max".
@@ -1383,6 +1403,10 @@ PAGE_SIZE multiple when read back.
        otherwise, a value change in this file generates a file
        modified event.
 
+         high
+               The number of times the cgroup's swap usage was over
+               the high threshold.
+
          max
                The number of times the cgroup's swap usage was about
                to go over the max boundary and swap allocation
index 2d01ce43d2a2cc56082b8f71fa412dda711e0f69..ebdecf864080282bb17045e92fc9d6baf3fd0c59 100644 (file)
@@ -105,7 +105,7 @@ References
 ----------
 
 - http://lkml.org/lkml/2007/2/12/6
-- Documentation/filesystems/proc.txt (1.8)
+- Documentation/filesystems/proc.rst (1.8)
 
 
 Thanks
index f83212fae4d536fbc8103c13d58d7930c0528af7..3eeeb488d95527022e4229dc939538ea20b58eeb 100644 (file)
@@ -268,7 +268,7 @@ Guest mitigation mechanisms
    /proc/irq/$NR/smp_affinity[_list] files. Limited documentation is
    available at:
 
-   https://www.kernel.org/doc/Documentation/IRQ-affinity.txt
+   https://www.kernel.org/doc/Documentation/core-api/irq/irq-affinity.rst
 
 .. _smt_control:
 
index e89d97f31eaf0302872ef812e79e30aa1e7f9dfa..41f06a09152e0fc7d21636c6f1b9ca776b46005c 100644 (file)
@@ -1,52 +1,48 @@
-Explaining the dreaded "No init found." boot hang message
+Explaining the "No working init found." boot hang message
 =========================================================
+:Authors: Andreas Mohr <andi at lisas period de>
+          Cristian Souza <cristianmsbr at gmail period com>
 
-OK, so you've got this pretty unintuitive message (currently located
-in init/main.c) and are wondering what the H*** went wrong.
-Some high-level reasons for failure (listed roughly in order of execution)
-to load the init binary are:
-
-A) Unable to mount root FS
-B) init binary doesn't exist on rootfs
-C) broken console device
-D) binary exists but dependencies not available
-E) binary cannot be loaded
-
-Detailed explanations:
-
-A) Set "debug" kernel parameter (in bootloader config file or CONFIG_CMDLINE)
-   to get more detailed kernel messages.
-B) make sure you have the correct root FS type
-   (and ``root=`` kernel parameter points to the correct partition),
-   required drivers such as storage hardware (such as SCSI or USB!)
-   and filesystem (ext3, jffs2 etc.) are builtin (alternatively as modules,
-   to be pre-loaded by an initrd)
-C) Possibly a conflict in ``console= setup`` --> initial console unavailable.
-   E.g. some serial consoles are unreliable due to serial IRQ issues (e.g.
-   missing interrupt-based configuration).
+This document provides some high-level reasons for failure
+(listed roughly in order of execution) to load the init binary.
+
+1) **Unable to mount root FS**: Set "debug" kernel parameter (in bootloader
+   config file or CONFIG_CMDLINE) to get more detailed kernel messages.
+
+2) **init binary doesn't exist on rootfs**: Make sure you have the correct
+   root FS type (and ``root=`` kernel parameter points to the correct
+   partition), required drivers such as storage hardware (such as SCSI or
+   USB!) and filesystem (ext3, jffs2, etc.) are builtin (alternatively as
+   modules, to be pre-loaded by an initrd).
+
+3) **Broken console device**: Possibly a conflict in ``console= setup``
+   --> initial console unavailable. E.g. some serial consoles are unreliable
+   due to serial IRQ issues (e.g. missing interrupt-based configuration).
    Try using a different ``console= device`` or e.g. ``netconsole=``.
-D) e.g. required library dependencies of the init binary such as
-   ``/lib/ld-linux.so.2`` missing or broken. Use
-   ``readelf -d <INIT>|grep NEEDED`` to find out which libraries are required.
-E) make sure the binary's architecture matches your hardware.
-   E.g. i386 vs. x86_64 mismatch, or trying to load x86 on ARM hardware.
-   In case you tried loading a non-binary file here (shell script?),
-   you should make sure that the script specifies an interpreter in its shebang
-   header line (``#!/...``) that is fully working (including its library
-   dependencies). And before tackling scripts, better first test a simple
-   non-script binary such as ``/bin/sh`` and confirm its successful execution.
-   To find out more, add code ``to init/main.c`` to display kernel_execve()s
-   return values.
+
+4) **Binary exists but dependencies not available**: E.g. required library
+   dependencies of the init binary such as ``/lib/ld-linux.so.2`` missing or
+   broken. Use ``readelf -d <INIT>|grep NEEDED`` to find out which libraries
+   are required.
+
+5) **Binary cannot be loaded**: Make sure the binary's architecture matches
+   your hardware. E.g. i386 vs. x86_64 mismatch, or trying to load x86 on ARM
+   hardware. In case you tried loading a non-binary file here (shell script?),
+   you should make sure that the script specifies an interpreter in its
+   shebang header line (``#!/...``) that is fully working (including its
+   library dependencies). And before tackling scripts, better first test a
+   simple non-script binary such as ``/bin/sh`` and confirm its successful
+   execution. To find out more, add code ``to init/main.c`` to display
+   kernel_execve()s return values.
 
 Please extend this explanation whenever you find new failure causes
 (after all loading the init binary is a CRITICAL and hard transition step
-which needs to be made as painless as possible), then submit patch to LKML.
+which needs to be made as painless as possible), then submit patch to LKML.
 Further TODOs:
 
 - Implement the various ``run_init_process()`` invocations via a struct array
   which can then store the ``kernel_execve()`` result value and on failure
   log it all by iterating over **all** results (very important usability fix).
-- try to make the implementation itself more helpful in general,
-  e.g. by providing additional error messages at affected places.
+- Try to make the implementation itself more helpful in general, e.g. by
+  providing additional error messages at affected places.
 
-Andreas Mohr <andi at lisas period de>
index 007a6b86e0eedc6b164a5734bd2acc7c37e11916..e4ee8b2db604fdd913f2366f371f89b0a0c9c370 100644 (file)
@@ -393,6 +393,12 @@ KERNELOFFSET
 The kernel randomization offset. Used to compute the page offset. If
 KASLR is disabled, this value is zero.
 
+KERNELPACMASK
+-------------
+
+The mask to extract the Pointer Authentication Code from a kernel virtual
+address.
+
 arm
 ===
 
index 7bc83f3d9bdfe376762ede68db1d2982eb4d586e..4379c6ac32659f10a639a16a7cb32d578f1e604d 100644 (file)
 
        initrd=         [BOOT] Specify the location of the initial ramdisk
 
+       initrdmem=      [KNL] Specify a physical address and size from which to
+                       load the initrd. If an initrd is compiled in or
+                       specified in the bootparams, it takes priority over this
+                       setting.
+                       Format: ss[KMG],nn[KMG]
+                       Default is 0, 0
+
        init_on_alloc=  [MM] Fill newly allocated pages and heap objects with
                        zeroes.
                        Format: 0 | 1
                        See Documentation/admin-guide/sysctl/vm.rst for details.
 
        ohci1394_dma=early      [HW] enable debugging via the ohci1394 driver.
-                       See Documentation/debugging-via-ohci1394.txt for more
+                       See Documentation/core-api/debugging-via-ohci1394.rst for more
                        info.
 
        olpc_ec_timeout= [OLPC] ms delay when issuing EC commands
                        Duration of CPU stall (s) to test RCU CPU stall
                        warnings, zero to disable.
 
+       rcutorture.stall_cpu_block= [KNL]
+                       Sleep while stalling if set.  This will result
+                       in warnings from preemptible RCU in addition
+                       to any other stall-related activity.
+
        rcutorture.stall_cpu_holdoff= [KNL]
                        Time to wait (s) after boot before inducing stall.
 
        rcutorture.stall_cpu_irqsoff= [KNL]
                        Disable interrupts while stalling if set.
 
+       rcutorture.stall_gp_kthread= [KNL]
+                       Duration (s) of forced sleep within RCU
+                       grace-period kthread to test RCU CPU stall
+                       warnings, zero to disable.  If both stall_cpu
+                       and stall_gp_kthread are specified, the
+                       kthread is starved first, then the CPU.
+
        rcutorture.stat_interval= [KNL]
                        Time (s) between statistics printk()s.
 
                        only normal grace-period primitives.  No effect
                        on CONFIG_TINY_RCU kernels.
 
+       rcupdate.rcu_task_ipi_delay= [KNL]
+                       Set time in jiffies during which RCU tasks will
+                       avoid sending IPIs, starting with the beginning
+                       of a given grace period.  Setting a large
+                       number avoids disturbing real-time workloads,
+                       but lengthens grace periods.
+
        rcupdate.rcu_task_stall_timeout= [KNL]
                        Set timeout in jiffies for RCU task stall warning
                        messages.  Disable with a value less than or equal
index 21818aca470868995fc4599e5ba73cac52955d51..dc36aeb65d0af6c7c1d789f0b1775a1531c428ca 100644 (file)
@@ -10,7 +10,7 @@ them to a "housekeeping" CPU dedicated to such work.
 References
 ==========
 
--      Documentation/IRQ-affinity.txt:  Binding interrupts to sets of CPUs.
+-      Documentation/core-api/irq/irq-affinity.rst:  Binding interrupts to sets of CPUs.
 
 -      Documentation/admin-guide/cgroup-v1:  Using cgroups to bind tasks to sets of CPUs.
 
index c30176e67900459e3d3ce4c634606a0642f17850..0bf49d7313ad8a6c3eed751dab224c301d7a3f7d 100644 (file)
@@ -12,107 +12,107 @@ and more generally they allow userland to take control of various
 memory page faults, something otherwise only the kernel code could do.
 
 For example userfaults allows a proper and more optimal implementation
-of the PROT_NONE+SIGSEGV trick.
+of the ``PROT_NONE+SIGSEGV`` trick.
 
 Design
 ======
 
-Userfaults are delivered and resolved through the userfaultfd syscall.
+Userfaults are delivered and resolved through the ``userfaultfd`` syscall.
 
-The userfaultfd (aside from registering and unregistering virtual
+The ``userfaultfd`` (aside from registering and unregistering virtual
 memory ranges) provides two primary functionalities:
 
-1) read/POLLIN protocol to notify a userland thread of the faults
+1) ``read/POLLIN`` protocol to notify a userland thread of the faults
    happening
 
-2) various UFFDIO_* ioctls that can manage the virtual memory regions
-   registered in the userfaultfd that allows userland to efficiently
+2) various ``UFFDIO_*`` ioctls that can manage the virtual memory regions
+   registered in the ``userfaultfd`` that allows userland to efficiently
    resolve the userfaults it receives via 1) or to manage the virtual
    memory in the background
 
 The real advantage of userfaults if compared to regular virtual memory
 management of mremap/mprotect is that the userfaults in all their
 operations never involve heavyweight structures like vmas (in fact the
-userfaultfd runtime load never takes the mmap_sem for writing).
+``userfaultfd`` runtime load never takes the mmap_sem for writing).
 
 Vmas are not suitable for page- (or hugepage) granular fault tracking
 when dealing with virtual address spaces that could span
 Terabytes. Too many vmas would be needed for that.
 
-The userfaultfd once opened by invoking the syscall, can also be
+The ``userfaultfd`` once opened by invoking the syscall, can also be
 passed using unix domain sockets to a manager process, so the same
 manager process could handle the userfaults of a multitude of
 different processes without them being aware about what is going on
-(well of course unless they later try to use the userfaultfd
+(well of course unless they later try to use the ``userfaultfd``
 themselves on the same region the manager is already tracking, which
-is a corner case that would currently return -EBUSY).
+is a corner case that would currently return ``-EBUSY``).
 
 API
 ===
 
-When first opened the userfaultfd must be enabled invoking the
-UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API (or
-a later API version) which will specify the read/POLLIN protocol
-userland intends to speak on the UFFD and the uffdio_api.features
-userland requires. The UFFDIO_API ioctl if successful (i.e. if the
-requested uffdio_api.api is spoken also by the running kernel and the
+When first opened the ``userfaultfd`` must be enabled invoking the
+``UFFDIO_API`` ioctl specifying a ``uffdio_api.api`` value set to ``UFFD_API`` (or
+a later API version) which will specify the ``read/POLLIN`` protocol
+userland intends to speak on the ``UFFD`` and the ``uffdio_api.features``
+userland requires. The ``UFFDIO_API`` ioctl if successful (i.e. if the
+requested ``uffdio_api.api`` is spoken also by the running kernel and the
 requested features are going to be enabled) will return into
-uffdio_api.features and uffdio_api.ioctls two 64bit bitmasks of
+``uffdio_api.features`` and ``uffdio_api.ioctls`` two 64bit bitmasks of
 respectively all the available features of the read(2) protocol and
 the generic ioctl available.
 
-The uffdio_api.features bitmask returned by the UFFDIO_API ioctl
-defines what memory types are supported by the userfaultfd and what
+The ``uffdio_api.features`` bitmask returned by the ``UFFDIO_API`` ioctl
+defines what memory types are supported by the ``userfaultfd`` and what
 events, except page fault notifications, may be generated.
 
-If the kernel supports registering userfaultfd ranges on hugetlbfs
-virtual memory areas, UFFD_FEATURE_MISSING_HUGETLBFS will be set in
-uffdio_api.features. Similarly, UFFD_FEATURE_MISSING_SHMEM will be
-set if the kernel supports registering userfaultfd ranges on shared
-memory (covering all shmem APIs, i.e. tmpfs, IPCSHM, /dev/zero
-MAP_SHARED, memfd_create, etc).
+If the kernel supports registering ``userfaultfd`` ranges on hugetlbfs
+virtual memory areas, ``UFFD_FEATURE_MISSING_HUGETLBFS`` will be set in
+``uffdio_api.features``. Similarly, ``UFFD_FEATURE_MISSING_SHMEM`` will be
+set if the kernel supports registering ``userfaultfd`` ranges on shared
+memory (covering all shmem APIs, i.e. tmpfs, ``IPCSHM``, ``/dev/zero``,
+``MAP_SHARED``, ``memfd_create``, etc).
 
-The userland application that wants to use userfaultfd with hugetlbfs
+The userland application that wants to use ``userfaultfd`` with hugetlbfs
 or shared memory need to set the corresponding flag in
-uffdio_api.features to enable those features.
+``uffdio_api.features`` to enable those features.
 
 If the userland desires to receive notifications for events other than
-page faults, it has to verify that uffdio_api.features has appropriate
-UFFD_FEATURE_EVENT_* bits set. These events are described in more
-detail below in "Non-cooperative userfaultfd" section.
-
-Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl should
-be invoked (if present in the returned uffdio_api.ioctls bitmask) to
-register a memory range in the userfaultfd by setting the
-uffdio_register structure accordingly. The uffdio_register.mode
+page faults, it has to verify that ``uffdio_api.features`` has appropriate
+``UFFD_FEATURE_EVENT_*`` bits set. These events are described in more
+detail below in `Non-cooperative userfaultfd`_ section.
+
+Once the ``userfaultfd`` has been enabled the ``UFFDIO_REGISTER`` ioctl should
+be invoked (if present in the returned ``uffdio_api.ioctls`` bitmask) to
+register a memory range in the ``userfaultfd`` by setting the
+uffdio_register structure accordingly. The ``uffdio_register.mode``
 bitmask will specify to the kernel which kind of faults to track for
-the range (UFFDIO_REGISTER_MODE_MISSING would track missing
-pages). The UFFDIO_REGISTER ioctl will return the
-uffdio_register.ioctls bitmask of ioctls that are suitable to resolve
+the range (``UFFDIO_REGISTER_MODE_MISSING`` would track missing
+pages). The ``UFFDIO_REGISTER`` ioctl will return the
+``uffdio_register.ioctls`` bitmask of ioctls that are suitable to resolve
 userfaults on the range registered. Not all ioctls will necessarily be
 supported for all memory types depending on the underlying virtual
 memory backend (anonymous memory vs tmpfs vs real filebacked
 mappings).
 
-Userland can use the uffdio_register.ioctls to manage the virtual
+Userland can use the ``uffdio_register.ioctls`` to manage the virtual
 address space in the background (to add or potentially also remove
-memory from the userfaultfd registered range). This means a userfault
+memory from the ``userfaultfd`` registered range). This means a userfault
 could be triggering just before userland maps in the background the
 user-faulted page.
 
-The primary ioctl to resolve userfaults is UFFDIO_COPY. That
+The primary ioctl to resolve userfaults is ``UFFDIO_COPY``. That
 atomically copies a page into the userfault registered range and wakes
-up the blocked userfaults (unless uffdio_copy.mode &
-UFFDIO_COPY_MODE_DONTWAKE is set). Other ioctl works similarly to
-UFFDIO_COPY. They're atomic as in guaranteeing that nothing can see an
-half copied page since it'll keep userfaulting until the copy has
-finished.
+up the blocked userfaults
+(unless ``uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE`` is set).
+Other ioctl works similarly to ``UFFDIO_COPY``. They're atomic as in
+guaranteeing that nothing can see an half copied page since it'll
+keep userfaulting until the copy has finished.
 
 Notes:
 
-- If you requested UFFDIO_REGISTER_MODE_MISSING when registering then
+- If you requested ``UFFDIO_REGISTER_MODE_MISSING`` when registering then
   you must provide some kind of page in your thread after reading from
-  the uffd.  You must provide either UFFDIO_COPY or UFFDIO_ZEROPAGE.
+  the uffd.  You must provide either ``UFFDIO_COPY`` or ``UFFDIO_ZEROPAGE``.
   The normal behavior of the OS automatically providing a zero page on
   an annonymous mmaping is not in place.
 
@@ -122,13 +122,13 @@ Notes:
 
 - You get the address of the access that triggered the missing page
   event out of a struct uffd_msg that you read in the thread from the
-  uffd.  You can supply as many pages as you want with UFFDIO_COPY or
-  UFFDIO_ZEROPAGE.  Keep in mind that unless you used DONTWAKE then
+  uffd.  You can supply as many pages as you want with ``UFFDIO_COPY`` or
+  ``UFFDIO_ZEROPAGE``.  Keep in mind that unless you used DONTWAKE then
   the first of any of those IOCTLs wakes up the faulting thread.
 
-- Be sure to test for all errors including (pollfd[0].revents &
-  POLLERR).  This can happen, e.g. when ranges supplied were
-  incorrect.
+- Be sure to test for all errors including
+  (``pollfd[0].revents & POLLERR``).  This can happen, e.g. when ranges
+  supplied were incorrect.
 
 Write Protect Notifications
 ---------------------------
@@ -136,41 +136,42 @@ Write Protect Notifications
 This is equivalent to (but faster than) using mprotect and a SIGSEGV
 signal handler.
 
-Firstly you need to register a range with UFFDIO_REGISTER_MODE_WP.
-Instead of using mprotect(2) you use ioctl(uffd, UFFDIO_WRITEPROTECT,
-struct *uffdio_writeprotect) while mode = UFFDIO_WRITEPROTECT_MODE_WP
+Firstly you need to register a range with ``UFFDIO_REGISTER_MODE_WP``.
+Instead of using mprotect(2) you use
+``ioctl(uffd, UFFDIO_WRITEPROTECT, struct *uffdio_writeprotect)``
+while ``mode = UFFDIO_WRITEPROTECT_MODE_WP``
 in the struct passed in.  The range does not default to and does not
 have to be identical to the range you registered with.  You can write
 protect as many ranges as you like (inside the registered range).
 Then, in the thread reading from uffd the struct will have
-msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP set. Now you send
-ioctl(uffd, UFFDIO_WRITEPROTECT, struct *uffdio_writeprotect) again
-while pagefault.mode does not have UFFDIO_WRITEPROTECT_MODE_WP set.
-This wakes up the thread which will continue to run with writes. This
+``msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP`` set. Now you send
+``ioctl(uffd, UFFDIO_WRITEPROTECT, struct *uffdio_writeprotect)``
+again while ``pagefault.mode`` does not have ``UFFDIO_WRITEPROTECT_MODE_WP``
+set. This wakes up the thread which will continue to run with writes. This
 allows you to do the bookkeeping about the write in the uffd reading
 thread before the ioctl.
 
-If you registered with both UFFDIO_REGISTER_MODE_MISSING and
-UFFDIO_REGISTER_MODE_WP then you need to think about the sequence in
+If you registered with both ``UFFDIO_REGISTER_MODE_MISSING`` and
+``UFFDIO_REGISTER_MODE_WP`` then you need to think about the sequence in
 which you supply a page and undo write protect.  Note that there is a
 difference between writes into a WP area and into a !WP area.  The
-former will have UFFD_PAGEFAULT_FLAG_WP set, the latter
-UFFD_PAGEFAULT_FLAG_WRITE.  The latter did not fail on protection but
-you still need to supply a page when UFFDIO_REGISTER_MODE_MISSING was
+former will have ``UFFD_PAGEFAULT_FLAG_WP`` set, the latter
+``UFFD_PAGEFAULT_FLAG_WRITE``.  The latter did not fail on protection but
+you still need to supply a page when ``UFFDIO_REGISTER_MODE_MISSING`` was
 used.
 
 QEMU/KVM
 ========
 
-QEMU/KVM is using the userfaultfd syscall to implement postcopy live
+QEMU/KVM is using the ``userfaultfd`` syscall to implement postcopy live
 migration. Postcopy live migration is one form of memory
 externalization consisting of a virtual machine running with part or
 all of its memory residing on a different node in the cloud. The
-userfaultfd abstraction is generic enough that not a single line of
+``userfaultfd`` abstraction is generic enough that not a single line of
 KVM kernel code had to be modified in order to add postcopy live
 migration to QEMU.
 
-Guest async page faults, FOLL_NOWAIT and all other GUP features work
+Guest async page faults, ``FOLL_NOWAIT`` and all other ``GUP*`` features work
 just fine in combination with userfaults. Userfaults trigger async
 page faults in the guest scheduler so those guest processes that
 aren't waiting for userfaults (i.e. network bound) can keep running in
@@ -183,19 +184,19 @@ generating userfaults for readonly guest regions.
 The implementation of postcopy live migration currently uses one
 single bidirectional socket but in the future two different sockets
 will be used (to reduce the latency of the userfaults to the minimum
-possible without having to decrease /proc/sys/net/ipv4/tcp_wmem).
+possible without having to decrease ``/proc/sys/net/ipv4/tcp_wmem``).
 
 The QEMU in the source node writes all pages that it knows are missing
 in the destination node, into the socket, and the migration thread of
-the QEMU running in the destination node runs UFFDIO_COPY|ZEROPAGE
-ioctls on the userfaultfd in order to map the received pages into the
-guest (UFFDIO_ZEROCOPY is used if the source page was a zero page).
+the QEMU running in the destination node runs ``UFFDIO_COPY|ZEROPAGE``
+ioctls on the ``userfaultfd`` in order to map the received pages into the
+guest (``UFFDIO_ZEROCOPY`` is used if the source page was a zero page).
 
 A different postcopy thread in the destination node listens with
-poll() to the userfaultfd in parallel. When a POLLIN event is
+poll() to the ``userfaultfd`` in parallel. When a ``POLLIN`` event is
 generated after a userfault triggers, the postcopy thread read() from
-the userfaultfd and receives the fault address (or -EAGAIN in case the
-userfault was already resolved and waken by a UFFDIO_COPY|ZEROPAGE run
+the ``userfaultfd`` and receives the fault address (or ``-EAGAIN`` in case the
+userfault was already resolved and waken by a ``UFFDIO_COPY|ZEROPAGE`` run
 by the parallel QEMU migration thread).
 
 After the QEMU postcopy thread (running in the destination node) gets
@@ -206,7 +207,7 @@ remaining missing pages from that new page offset. Soon after that
 (just the time to flush the tcp_wmem queue through the network) the
 migration thread in the QEMU running in the destination node will
 receive the page that triggered the userfault and it'll map it as
-usual with the UFFDIO_COPY|ZEROPAGE (without actually knowing if it
+usual with the ``UFFDIO_COPY|ZEROPAGE`` (without actually knowing if it
 was spontaneously sent by the source or if it was an urgent page
 requested through a userfault).
 
@@ -219,74 +220,74 @@ checked to find which missing pages to send in round robin and we seek
 over it when receiving incoming userfaults. After sending each page of
 course the bitmap is updated accordingly. It's also useful to avoid
 sending the same page twice (in case the userfault is read by the
-postcopy thread just before UFFDIO_COPY|ZEROPAGE runs in the migration
+postcopy thread just before ``UFFDIO_COPY|ZEROPAGE`` runs in the migration
 thread).
 
 Non-cooperative userfaultfd
 ===========================
 
-When the userfaultfd is monitored by an external manager, the manager
+When the ``userfaultfd`` is monitored by an external manager, the manager
 must be able to track changes in the process virtual memory
 layout. Userfaultfd can notify the manager about such changes using
 the same read(2) protocol as for the page fault notifications. The
 manager has to explicitly enable these events by setting appropriate
-bits in uffdio_api.features passed to UFFDIO_API ioctl:
+bits in ``uffdio_api.features`` passed to ``UFFDIO_API`` ioctl:
 
-UFFD_FEATURE_EVENT_FORK
-       enable userfaultfd hooks for fork(). When this feature is
-       enabled, the userfaultfd context of the parent process is
+``UFFD_FEATURE_EVENT_FORK``
+       enable ``userfaultfd`` hooks for fork(). When this feature is
+       enabled, the ``userfaultfd`` context of the parent process is
        duplicated into the newly created process. The manager
-       receives UFFD_EVENT_FORK with file descriptor of the new
-       userfaultfd context in the uffd_msg.fork.
+       receives ``UFFD_EVENT_FORK`` with file descriptor of the new
+       ``userfaultfd`` context in the ``uffd_msg.fork``.
 
-UFFD_FEATURE_EVENT_REMAP
+``UFFD_FEATURE_EVENT_REMAP``
        enable notifications about mremap() calls. When the
        non-cooperative process moves a virtual memory area to a
        different location, the manager will receive
-       UFFD_EVENT_REMAP. The uffd_msg.remap will contain the old and
+       ``UFFD_EVENT_REMAP``. The ``uffd_msg.remap`` will contain the old and
        new addresses of the area and its original length.
 
-UFFD_FEATURE_EVENT_REMOVE
+``UFFD_FEATURE_EVENT_REMOVE``
        enable notifications about madvise(MADV_REMOVE) and
-       madvise(MADV_DONTNEED) calls. The event UFFD_EVENT_REMOVE will
-       be generated upon these calls to madvise. The uffd_msg.remove
+       madvise(MADV_DONTNEED) calls. The event ``UFFD_EVENT_REMOVE`` will
+       be generated upon these calls to madvise(). The ``uffd_msg.remove``
        will contain start and end addresses of the removed area.
 
-UFFD_FEATURE_EVENT_UNMAP
+``UFFD_FEATURE_EVENT_UNMAP``
        enable notifications about memory unmapping. The manager will
-       get UFFD_EVENT_UNMAP with uffd_msg.remove containing start and
+       get ``UFFD_EVENT_UNMAP`` with ``uffd_msg.remove`` containing start and
        end addresses of the unmapped area.
 
-Although the UFFD_FEATURE_EVENT_REMOVE and UFFD_FEATURE_EVENT_UNMAP
+Although the ``UFFD_FEATURE_EVENT_REMOVE`` and ``UFFD_FEATURE_EVENT_UNMAP``
 are pretty similar, they quite differ in the action expected from the
-userfaultfd manager. In the former case, the virtual memory is
+``userfaultfd`` manager. In the former case, the virtual memory is
 removed, but the area is not, the area remains monitored by the
-userfaultfd, and if a page fault occurs in that area it will be
+``userfaultfd``, and if a page fault occurs in that area it will be
 delivered to the manager. The proper resolution for such page fault is
 to zeromap the faulting address. However, in the latter case, when an
 area is unmapped, either explicitly (with munmap() system call), or
 implicitly (e.g. during mremap()), the area is removed and in turn the
-userfaultfd context for such area disappears too and the manager will
+``userfaultfd`` context for such area disappears too and the manager will
 not get further userland page faults from the removed area. Still, the
 notification is required in order to prevent manager from using
-UFFDIO_COPY on the unmapped area.
+``UFFDIO_COPY`` on the unmapped area.
 
 Unlike userland page faults which have to be synchronous and require
 explicit or implicit wakeup, all the events are delivered
 asynchronously and the non-cooperative process resumes execution as
-soon as manager executes read(). The userfaultfd manager should
-carefully synchronize calls to UFFDIO_COPY with the events
-processing. To aid the synchronization, the UFFDIO_COPY ioctl will
-return -ENOSPC when the monitored process exits at the time of
-UFFDIO_COPY, and -ENOENT, when the non-cooperative process has changed
-its virtual memory layout simultaneously with outstanding UFFDIO_COPY
+soon as manager executes read(). The ``userfaultfd`` manager should
+carefully synchronize calls to ``UFFDIO_COPY`` with the events
+processing. To aid the synchronization, the ``UFFDIO_COPY`` ioctl will
+return ``-ENOSPC`` when the monitored process exits at the time of
+``UFFDIO_COPY``, and ``-ENOENT``, when the non-cooperative process has changed
+its virtual memory layout simultaneously with outstanding ``UFFDIO_COPY``
 operation.
 
 The current asynchronous model of the event delivery is optimal for
-single threaded non-cooperative userfaultfd manager implementations. A
+single threaded non-cooperative ``userfaultfd`` manager implementations. A
 synchronous event delivery model can be added later as a new
-userfaultfd feature to facilitate multithreading enhancements of the
-non cooperative manager, for example to allow UFFDIO_COPY ioctls to
+``userfaultfd`` feature to facilitate multithreading enhancements of the
+non cooperative manager, for example to allow ``UFFDIO_COPY`` ioctls to
 run in parallel to the event reception. Single threaded
 implementations should continue to use the current async event
 delivery model instead.
index 82a4fda057f986012b76424623869fd854b72d59..c6772075c80cce62953e559c7bff98379a1040f5 100644 (file)
@@ -18,7 +18,7 @@ Mounting the root filesystem via NFS (nfsroot)
 In order to use a diskless system, such as an X-terminal or printer server for
 example, it is necessary for the root filesystem to be present on a non-disk
 device. This may be an initramfs (see
-Documentation/filesystems/ramfs-rootfs-initramfs.txt), a ramdisk (see
+Documentation/filesystems/ramfs-rootfs-initramfs.rst), a ramdisk (see
 Documentation/admin-guide/initrd.rst) or a filesystem mounted via NFS. The
 following text describes on how to use NFS for the root filesystem. For the rest
 of this text 'client' means the diskless system, and 'server' means the NFS
index aaf1667489f871ba47c9e68a109f0832b9713919..08ec2c2bdce3bfeac1250b3b7febca235d164f90 100644 (file)
@@ -6,6 +6,21 @@ Numa policy hit/miss statistics
 
 All units are pages. Hugepages have separate counters.
 
+The numa_hit, numa_miss and numa_foreign counters reflect how well processes
+are able to allocate memory from nodes they prefer. If they succeed, numa_hit
+is incremented on the preferred node, otherwise numa_foreign is incremented on
+the preferred node and numa_miss on the node where allocation succeeded.
+
+Usually preferred node is the one local to the CPU where the process executes,
+but restrictions such as mempolicies can change that, so there are also two
+counters based on CPU local node. local_node is similar to numa_hit and is
+incremented on allocation from a node by CPU on the same node. other_node is
+similar to numa_miss and is incremented on the node where allocation succeeds
+from a CPU from a different node. Note there is no counter analogical to
+numa_foreign.
+
+In more detail:
+
 =============== ============================================================
 numa_hit       A process wanted to allocate memory from this node,
                and succeeded.
@@ -14,11 +29,13 @@ numa_miss   A process wanted to allocate memory from another node,
                but ended up with memory from this node.
 
 numa_foreign   A process wanted to allocate on this node,
-               but ended up with memory from another one.
+               but ended up with memory from another node.
 
-local_node     A process ran on this node and got memory from it.
+local_node     A process ran on this node's CPU,
+               and got memory from this node.
 
-other_node     A process ran on this node and got memory from another node.
+other_node     A process ran on a different node's CPU
+               and got memory from this node.
 
 interleave_hit         Interleaving wanted to allocate from this node
                and succeeded.
@@ -28,3 +45,11 @@ For easier reading you can use the numastat utility from the numactl package
 (http://oss.sgi.com/projects/libnuma/). Note that it only works
 well right now on machines with a small number of CPUs.
 
+Note that on systems with memoryless nodes (where a node has CPUs but no
+memory) the numa_hit, numa_miss and numa_foreign statistics can be skewed
+heavily. In the current kernel implementation, if a process prefers a
+memoryless node (i.e.  because it is running on one of its local CPU), the
+implementation actually treats one of the nearest nodes with memory as the
+preferred node. As a result, such allocation will not increase the numa_foreign
+counter on the memoryless node, and will skew the numa_hit, numa_miss and
+numa_foreign statistics of the nearest node.
index 72effa7c23b97ba8c2052f458ca9645b7caab6a2..1307b5274a0f93e5650b9c31f5ee6908aebfefc3 100644 (file)
@@ -1,6 +1,6 @@
 .. _perf_security:
 
-Perf Events and tool security
+Perf events and tool security
 =============================
 
 Overview
@@ -42,11 +42,11 @@ categories:
 Data that belong to the fourth category can potentially contain
 sensitive process data. If PMUs in some monitoring modes capture values
 of execution context registers or data from process memory then access
-to such monitoring capabilities requires to be ordered and secured
-properly. So, perf_events/Perf performance monitoring is the subject for
-security access control management [5]_ .
+to such monitoring modes requires to be ordered and secured properly.
+So, perf_events performance monitoring and observability operations are
+the subject for security access control management [5]_ .
 
-perf_events/Perf access control
+perf_events access control
 -------------------------------
 
 To perform security checks, the Linux implementation splits processes
@@ -66,11 +66,25 @@ into distinct units, known as capabilities [6]_ , which can be
 independently enabled and disabled on per-thread basis for processes and
 files of unprivileged users.
 
-Unprivileged processes with enabled CAP_SYS_ADMIN capability are treated
+Unprivileged processes with enabled CAP_PERFMON capability are treated
 as privileged processes with respect to perf_events performance
-monitoring and bypass *scope* permissions checks in the kernel.
-
-Unprivileged processes using perf_events system call API is also subject
+monitoring and observability operations, thus, bypass *scope* permissions
+checks in the kernel. CAP_PERFMON implements the principle of least
+privilege [13]_ (POSIX 1003.1e: 2.2.2.39) for performance monitoring and
+observability operations in the kernel and provides a secure approach to
+perfomance monitoring and observability in the system.
+
+For backward compatibility reasons the access to perf_events monitoring and
+observability operations is also open for CAP_SYS_ADMIN privileged
+processes but CAP_SYS_ADMIN usage for secure monitoring and observability
+use cases is discouraged with respect to the CAP_PERFMON capability.
+If system audit records [14]_ for a process using perf_events system call
+API contain denial records of acquiring both CAP_PERFMON and CAP_SYS_ADMIN
+capabilities then providing the process with CAP_PERFMON capability singly
+is recommended as the preferred secure approach to resolve double access
+denial logging related to usage of performance monitoring and observability.
+
+Unprivileged processes using perf_events system call are also subject
 for PTRACE_MODE_READ_REALCREDS ptrace access mode check [7]_ , whose
 outcome determines whether monitoring is permitted. So unprivileged
 processes provided with CAP_SYS_PTRACE capability are effectively
@@ -82,14 +96,14 @@ performance analysis of monitored processes or a system. For example,
 CAP_SYSLOG capability permits reading kernel space memory addresses from
 /proc/kallsyms file.
 
-perf_events/Perf privileged users
+Privileged Perf users groups
 ---------------------------------
 
 Mechanisms of capabilities, privileged capability-dumb files [6]_ and
-file system ACLs [10]_ can be used to create a dedicated group of
-perf_events/Perf privileged users who are permitted to execute
-performance monitoring without scope limits. The following steps can be
-taken to create such a group of privileged Perf users.
+file system ACLs [10]_ can be used to create dedicated groups of
+privileged Perf users who are permitted to execute performance monitoring
+and observability without scope limits. The following steps can be
+taken to create such groups of privileged Perf users.
 
 1. Create perf_users group of privileged Perf users, assign perf_users
    group to Perf tool executable and limit access to the executable for
@@ -108,30 +122,51 @@ taken to create such a group of privileged Perf users.
    -rwxr-x---  2 root perf_users  11M Oct 19 15:12 perf
 
 2. Assign the required capabilities to the Perf tool executable file and
-   enable members of perf_users group with performance monitoring
+   enable members of perf_users group with monitoring and observability
    privileges [6]_ :
 
 ::
 
-   # setcap "cap_sys_admin,cap_sys_ptrace,cap_syslog=ep" perf
-   # setcap -v "cap_sys_admin,cap_sys_ptrace,cap_syslog=ep" perf
+   # setcap "cap_perfmon,cap_sys_ptrace,cap_syslog=ep" perf
+   # setcap -v "cap_perfmon,cap_sys_ptrace,cap_syslog=ep" perf
    perf: OK
    # getcap perf
-   perf = cap_sys_ptrace,cap_sys_admin,cap_syslog+ep
+   perf = cap_sys_ptrace,cap_syslog,cap_perfmon+ep
+
+If the libcap installed doesn't yet support "cap_perfmon", use "38" instead,
+i.e.:
+
+::
+
+   # setcap "38,cap_ipc_lock,cap_sys_ptrace,cap_syslog=ep" perf
+
+Note that you may need to have 'cap_ipc_lock' in the mix for tools such as
+'perf top', alternatively use 'perf top -m N', to reduce the memory that
+it uses for the perf ring buffer, see the memory allocation section below.
+
+Using a libcap without support for CAP_PERFMON will make cap_get_flag(caps, 38,
+CAP_EFFECTIVE, &val) fail, which will lead the default event to be 'cycles:u',
+so as a workaround explicitly ask for the 'cycles' event, i.e.:
+
+::
+
+  # perf top -e cycles
+
+To get kernel and user samples with a perf binary with just CAP_PERFMON.
 
 As a result, members of perf_users group are capable of conducting
-performance monitoring by using functionality of the configured Perf
-tool executable that, when executes, passes perf_events subsystem scope
-checks.
+performance monitoring and observability by using functionality of the
+configured Perf tool executable that, when executes, passes perf_events
+subsystem scope checks.
 
 This specific access control management is only available to superuser
 or root running processes with CAP_SETPCAP, CAP_SETFCAP [6]_
 capabilities.
 
-perf_events/Perf unprivileged users
+Unprivileged users
 -----------------------------------
 
-perf_events/Perf *scope* and *access* control for unprivileged processes
+perf_events *scope* and *access* control for unprivileged processes
 is governed by perf_event_paranoid [2]_ setting:
 
 -1:
@@ -166,7 +201,7 @@ is governed by perf_event_paranoid [2]_ setting:
      perf_event_mlock_kb locking limit is imposed but ignored for
      unprivileged processes with CAP_IPC_LOCK capability.
 
-perf_events/Perf resource control
+Resource control
 ---------------------------------
 
 Open file descriptors
@@ -227,4 +262,5 @@ Bibliography
 .. [10] `<http://man7.org/linux/man-pages/man5/acl.5.html>`_
 .. [11] `<http://man7.org/linux/man-pages/man2/getrlimit.2.html>`_
 .. [12] `<http://man7.org/linux/man-pages/man5/limits.conf.5.html>`_
-
+.. [13] `<https://sites.google.com/site/fullycapable>`_
+.. [14] `<http://man7.org/linux/man-pages/man8/auditd.8.html>`_
index 5605cc6f95605b5cf2489f88c42655b1696898f2..a96a423e37791862339b2303b555f5b5a2a6ac99 100644 (file)
@@ -159,17 +159,15 @@ governor uses that information depends on what algorithm is implemented by it
 and that is the primary reason for having more than one governor in the
 ``CPUIdle`` subsystem.
 
-There are three ``CPUIdle`` governors available, ``menu``, `TEO <teo-gov_>`_
-and ``ladder``.  Which of them is used by default depends on the configuration
-of the kernel and in particular on whether or not the scheduler tick can be
-`stopped by the idle loop <idle-cpus-and-tick_>`_.  It is possible to change the
-governor at run time if the ``cpuidle_sysfs_switch`` command line parameter has
-been passed to the kernel, but that is not safe in general, so it should not be
-done on production systems (that may change in the future, though).  The name of
-the ``CPUIdle`` governor currently used by the kernel can be read from the
-:file:`current_governor_ro` (or :file:`current_governor` if
-``cpuidle_sysfs_switch`` is present in the kernel command line) file under
-:file:`/sys/devices/system/cpu/cpuidle/` in ``sysfs``.
+There are four ``CPUIdle`` governors available, ``menu``, `TEO <teo-gov_>`_,
+``ladder`` and ``haltpoll``.  Which of them is used by default depends on the
+configuration of the kernel and in particular on whether or not the scheduler
+tick can be `stopped by the idle loop <idle-cpus-and-tick_>`_.  Available
+governors can be read from the :file:`available_governors`, and the governor
+can be changed at runtime.  The name of the ``CPUIdle`` governor currently
+used by the kernel can be read from the :file:`current_governor_ro` or
+:file:`current_governor` file under :file:`/sys/devices/system/cpu/cpuidle/`
+in ``sysfs``.
 
 Which ``CPUIdle`` driver is used, on the other hand, usually depends on the
 platform the kernel is running on, but there are platforms with more than one
diff --git a/Documentation/admin-guide/pm/intel-speed-select.rst b/Documentation/admin-guide/pm/intel-speed-select.rst
new file mode 100644 (file)
index 0000000..b2ca601
--- /dev/null
@@ -0,0 +1,917 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================================
+Intel(R) Speed Select Technology User Guide
+============================================================
+
+The Intel(R) Speed Select Technology (Intel(R) SST) provides a powerful new
+collection of features that give more granular control over CPU performance.
+With Intel(R) SST, one server can be configured for power and performance for a
+variety of diverse workload requirements.
+
+Refer to the links below for an overview of the technology:
+
+- https://www.intel.com/content/www/us/en/architecture-and-technology/speed-select-technology-article.html
+- https://builders.intel.com/docs/networkbuilders/intel-speed-select-technology-base-frequency-enhancing-performance.pdf
+
+These capabilities are further enhanced in some of the newer generations of
+server platforms where these features can be enumerated and controlled
+dynamically without pre-configuring via BIOS setup options. This dynamic
+configuration is done via mailbox commands to the hardware. One way to enumerate
+and configure these features is by using the Intel Speed Select utility.
+
+This document explains how to use the Intel Speed Select tool to enumerate and
+control Intel(R) SST features. This document gives example commands and explains
+how these commands change the power and performance profile of the system under
+test. Using this tool as an example, customers can replicate the messaging
+implemented in the tool in their production software.
+
+intel-speed-select configuration tool
+======================================
+
+Most Linux distribution packages may include the "intel-speed-select" tool. If not,
+it can be built by downloading the Linux kernel tree from kernel.org. Once
+downloaded, the tool can be built without building the full kernel.
+
+From the kernel tree, run the following commands::
+
+# cd tools/power/x86/intel-speed-select/
+# make
+# make install
+
+Getting Help
+------------
+
+To get help with the tool, execute the command below::
+
+# intel-speed-select --help
+
+The top-level help describes arguments and features. Notice that there is a
+multi-level help structure in the tool. For example, to get help for the feature "perf-profile"::
+
+# intel-speed-select perf-profile --help
+
+To get help on a command, another level of help is provided. For example for the command info "info"::
+
+# intel-speed-select perf-profile info --help
+
+Summary of platform capability
+------------------------------
+To check the current platform and driver capaibilities, execute::
+
+#intel-speed-select --info
+
+For example on a test system::
+
+ # intel-speed-select --info
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ Platform: API version : 1
+ Platform: Driver version : 1
+ Platform: mbox supported : 1
+ Platform: mmio supported : 1
+ Intel(R) SST-PP (feature perf-profile) is supported
+ TDP level change control is unlocked, max level: 4
+ Intel(R) SST-TF (feature turbo-freq) is supported
+ Intel(R) SST-BF (feature base-freq) is not supported
+ Intel(R) SST-CP (feature core-power) is supported
+
+Intel(R) Speed Select Technology - Performance Profile (Intel(R) SST-PP)
+------------------------------------------------------------------------
+
+This feature allows configuration of a server dynamically based on workload
+performance requirements. This helps users during deployment as they do not have
+to choose a specific server configuration statically.  This Intel(R) Speed Select
+Technology - Performance Profile (Intel(R) SST-PP) feature introduces a mechanism
+that allows multiple optimized performance profiles per system. Each profile
+defines a set of CPUs that need to be online and rest offline to sustain a
+guaranteed base frequency. Once the user issues a command to use a specific
+performance profile and meet CPU online/offline requirement, the user can expect
+a change in the base frequency dynamically. This feature is called
+"perf-profile" when using the Intel Speed Select tool.
+
+Number or performance levels
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There can be multiple performance profiles on a system. To get the number of
+profiles, execute the command below::
+
+ # intel-speed-select perf-profile get-config-levels
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+        get-config-levels:4
+ package-1
+  die-0
+    cpu-14
+        get-config-levels:4
+
+On this system under test, there are 4 performance profiles in addition to the
+base performance profile (which is performance level 0).
+
+Lock/Unlock status
+~~~~~~~~~~~~~~~~~~
+
+Even if there are multiple performance profiles, it is possible that that they
+are locked. If they are locked, users cannot issue a command to change the
+performance state. It is possible that there is a BIOS setup to unlock or check
+with your system vendor.
+
+To check if the system is locked, execute the following command::
+
+ # intel-speed-select perf-profile get-lock-status
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+        get-lock-status:0
+ package-1
+  die-0
+    cpu-14
+        get-lock-status:0
+
+In this case, lock status is 0, which means that the system is unlocked.
+
+Properties of a performance level
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get properties of a specific performance level (For example for the level 0, below), execute the command below::
+
+ # intel-speed-select perf-profile info -l 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      perf-profile-level-0
+        cpu-count:28
+        enable-cpu-mask:000003ff,f0003fff
+        enable-cpu-list:0,1,2,3,4,5,6,7,8,9,10,11,12,13,28,29,30,31,32,33,34,35,36,37,38,39,40,41
+        thermal-design-power-ratio:26
+        base-frequency(MHz):2600
+        speed-select-turbo-freq:disabled
+        speed-select-base-freq:disabled
+       ...
+       ...
+
+Here -l option is used to specify a performance level.
+
+If the option -l is omitted, then this command will print information about all
+the performance levels. The above command is printing properties of the
+performance level 0.
+
+For this performance profile, the list of CPUs displayed by the
+"enable-cpu-mask/enable-cpu-list" at the max can be "online." When that
+condition is met, then base frequency of 2600 MHz can be maintained. To
+understand more, execute "intel-speed-select perf-profile info" for performance
+level 4::
+
+ # intel-speed-select perf-profile info -l 4
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      perf-profile-level-4
+        cpu-count:28
+        enable-cpu-mask:000000fa,f0000faf
+        enable-cpu-list:0,1,2,3,5,7,8,9,10,11,28,29,30,31,33,35,36,37,38,39
+        thermal-design-power-ratio:28
+        base-frequency(MHz):2800
+        speed-select-turbo-freq:disabled
+        speed-select-base-freq:unsupported
+       ...
+       ...
+
+There are fewer CPUs in the "enable-cpu-mask/enable-cpu-list". Consequently, if
+the user only keeps these CPUs online and the rest "offline," then the base
+frequency is increased to 2.8 GHz compared to 2.6 GHz at performance level 0.
+
+Get current performance level
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the current performance level, execute::
+
+ # intel-speed-select perf-profile get-config-current-level
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+        get-config-current_level:0
+
+First verify that the base_frequency displayed by the cpufreq sysfs is correct::
+
+ # cat /sys/devices/system/cpu/cpu0/cpufreq/base_frequency
+ 2600000
+
+This matches the base-frequency (MHz) field value displayed from the
+"perf-profile info" command for performance level 0(cpufreq frequency is in
+KHz).
+
+To check if the average frequency is equal to the base frequency for a 100% busy
+workload, disable turbo::
+
+# echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
+
+Then runs a busy workload on all CPUs, for example::
+
+#stress -c 64
+
+To verify the base frequency, run turbostat::
+
+ #turbostat -c 0-13 --show Package,Core,CPU,Bzy_MHz -i 1
+
+  Package      Core    CPU     Bzy_MHz
+               -       -       2600
+  0            0       0       2600
+  0            1       1       2600
+  0            2       2       2600
+  0            3       3       2600
+  0            4       4       2600
+  .            .       .       .
+
+
+Changing performance level
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To the change the performance level to 4, execute::
+
+ # intel-speed-select -d perf-profile set-config-level -l 4 -o
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      perf-profile
+        set_tdp_level:success
+
+In the command above, "-o" is optional. If it is specified, then it will also
+offline CPUs which are not present in the enable_cpu_mask for this performance
+level.
+
+Now if the base_frequency is checked::
+
+ #cat /sys/devices/system/cpu/cpu0/cpufreq/base_frequency
+ 2800000
+
+Which shows that the base frequency now increased from 2600 MHz at performance
+level 0 to 2800 MHz at performance level 4. As a result, any workload, which can
+use fewer CPUs, can see a boost of 200 MHz compared to performance level 0.
+
+Check presence of other Intel(R) SST features
+---------------------------------------------
+
+Each of the performance profiles also specifies weather there is support of
+other two Intel(R) SST features (Intel(R) Speed Select Technology - Base Frequency
+(Intel(R) SST-BF) and Intel(R) Speed Select Technology - Turbo Frequency (Intel
+SST-TF)).
+
+For example, from the output of "perf-profile info" above, for level 0 and level
+4:
+
+For level 0::
+       speed-select-turbo-freq:disabled
+       speed-select-base-freq:disabled
+
+For level 4::
+       speed-select-turbo-freq:disabled
+       speed-select-base-freq:unsupported
+
+Given these results, the "speed-select-base-freq" (Intel(R) SST-BF) in level 4
+changed from "disabled" to "unsupported" compared to performance level 0.
+
+This means that at performance level 4, the "speed-select-base-freq" feature is
+not supported. However, at performance level 0, this feature is "supported", but
+currently "disabled", meaning the user has not activated this feature. Whereas
+"speed-select-turbo-freq" (Intel(R) SST-TF) is supported at both performance
+levels, but currently not activated by the user.
+
+The Intel(R) SST-BF and the Intel(R) SST-TF features are built on a foundation
+technology called Intel(R) Speed Select Technology - Core Power (Intel(R) SST-CP).
+The platform firmware enables this feature when Intel(R) SST-BF or Intel(R) SST-TF
+is supported on a platform.
+
+Intel(R) Speed Select Technology Core Power (Intel(R) SST-CP)
+---------------------------------------------------------------
+
+Intel(R) Speed Select Technology Core Power (Intel(R) SST-CP) is an interface that
+allows users to define per core priority. This defines a mechanism to distribute
+power among cores when there is a power constrained scenario. This defines a
+class of service (CLOS) configuration.
+
+The user can configure up to 4 class of service configurations. Each CLOS group
+configuration allows definitions of parameters, which affects how the frequency
+can be limited and power is distributed. Each CPU core can be tied to a class of
+service and hence an associated priority. The granularity is at core level not
+at per CPU level.
+
+Enable CLOS based prioritization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To use CLOS based prioritization feature, firmware must be informed to enable
+and use a priority type. There is a default per platform priority type, which
+can be changed with optional command line parameter.
+
+To enable and check the options, execute::
+
+ # intel-speed-select core-power enable --help
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ Enable core-power for a package/die
+       Clos Enable: Specify priority type with [--priority|-p]
+                0: Proportional, 1: Ordered
+
+There are two types of priority types:
+
+- Ordered
+
+Priority for ordered throttling is defined based on the index of the assigned
+CLOS group. Where CLOS0 gets highest priority (throttled last).
+
+Priority order is:
+CLOS0 > CLOS1 > CLOS2 > CLOS3.
+
+- Proportional
+
+When proportional priority is used, there is an additional parameter called
+frequency_weight, which can be specified per CLOS group. The goal of
+proportional priority is to provide each core with the requested min., then
+distribute all remaining (excess/deficit) budgets in proportion to a defined
+weight. This proportional priority can be configured using "core-power config"
+command.
+
+To enable with the platform default priority type, execute::
+
+ # intel-speed-select core-power enable
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      core-power
+        enable:success
+ package-1
+  die-0
+    cpu-6
+      core-power
+        enable:success
+
+The scope of this enable is per package or die scoped when a package contains
+multiple dies. To check if CLOS is enabled and get priority type, "core-power
+info" command can be used. For example to check the status of core-power feature
+on CPU 0, execute::
+
+ # intel-speed-select -c 0 core-power info
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      core-power
+        support-status:supported
+        enable-status:enabled
+        clos-enable-status:enabled
+        priority-type:proportional
+ package-1
+  die-0
+    cpu-24
+      core-power
+        support-status:supported
+        enable-status:enabled
+        clos-enable-status:enabled
+        priority-type:proportional
+
+Configuring CLOS groups
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Each CLOS group has its own attributes including min, max, freq_weight and
+desired. These parameters can be configured with "core-power config" command.
+Defaults will be used if user skips setting a parameter except clos id, which is
+mandatory. To check core-power config options, execute::
+
+ # intel-speed-select core-power config --help
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ Set core-power configuration for one of the four clos ids
+       Specify targeted clos id with [--clos|-c]
+       Specify clos Proportional Priority [--weight|-w]
+       Specify clos min in MHz with [--min|-n]
+       Specify clos max in MHz with [--max|-m]
+
+For example::
+
+ # intel-speed-select core-power config -c 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ clos epp is not specified, default: 0
+ clos frequency weight is not specified, default: 0
+ clos min is not specified, default: 0 MHz
+ clos max is not specified, default: 25500 MHz
+ clos desired is not specified, default: 0
+ package-0
+  die-0
+    cpu-0
+      core-power
+        config:success
+ package-1
+  die-0
+    cpu-6
+      core-power
+        config:success
+
+The user has the option to change defaults. For example, the user can change the
+"min" and set the base frequency to always get guaranteed base frequency.
+
+Get the current CLOS configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To check the current configuration, "core-power get-config" can be used. For
+example, to get the configuration of CLOS 0::
+
+ # intel-speed-select core-power get-config -c 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      core-power
+        clos:0
+        epp:0
+        clos-proportional-priority:0
+        clos-min:0 MHz
+        clos-max:Max Turbo frequency
+        clos-desired:0 MHz
+ package-1
+  die-0
+    cpu-24
+      core-power
+        clos:0
+        epp:0
+        clos-proportional-priority:0
+        clos-min:0 MHz
+        clos-max:Max Turbo frequency
+        clos-desired:0 MHz
+
+Associating a CPU with a CLOS group
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To associate a CPU to a CLOS group "core-power assoc" command can be used::
+
+ # intel-speed-select core-power assoc --help
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ Associate a clos id to a CPU
+       Specify targeted clos id with [--clos|-c]
+
+
+For example to associate CPU 10 to CLOS group 3, execute::
+
+ # intel-speed-select -c 10 core-power assoc -c 3
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-10
+      core-power
+        assoc:success
+
+Once a CPU is associated, its sibling CPUs are also associated to a CLOS group.
+Once associated, avoid changing Linux "cpufreq" subsystem scaling frequency
+limits.
+
+To check the existing association for a CPU, "core-power get-assoc" command can
+be used. For example, to get association of CPU 10, execute::
+
+ # intel-speed-select -c 10 core-power get-assoc
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-1
+  die-0
+    cpu-10
+      get-assoc
+        clos:3
+
+This shows that CPU 10 is part of a CLOS group 3.
+
+
+Disable CLOS based prioritization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To disable, execute::
+
+# intel-speed-select core-power disable
+
+Some features like Intel(R) SST-TF can only be enabled when CLOS based prioritization
+is enabled. For this reason, disabling while Intel(R) SST-TF is enabled can cause
+Intel(R) SST-TF to fail. This will cause the "disable" command to display an error
+if Intel(R) SST-TF is already enabled. In turn, to disable, the Intel(R) SST-TF
+feature must be disabled first.
+
+Intel(R) Speed Select Technology - Base Frequency (Intel(R) SST-BF)
+-------------------------------------------------------------------
+
+The Intel(R) Speed Select Technology - Base Frequency (Intel(R) SST-BF) feature lets
+the user control base frequency. If some critical workload threads demand
+constant high guaranteed performance, then this feature can be used to execute
+the thread at higher base frequency on specific sets of CPUs (high priority
+CPUs) at the cost of lower base frequency (low priority CPUs) on other CPUs.
+This feature does not require offline of the low priority CPUs.
+
+The support of Intel(R) SST-BF depends on the Intel(R) Speed Select Technology -
+Performance Profile (Intel(R) SST-PP) performance level configuration. It is
+possible that only certain performance levels support Intel(R) SST-BF. It is also
+possible that only base performance level (level = 0) has support of Intel
+SST-BF. Consequently, first select the desired performance level to enable this
+feature.
+
+In the system under test here, Intel(R) SST-BF is supported at the base
+performance level 0, but currently disabled. For example for the level 0::
+
+ # intel-speed-select -c 0 perf-profile info -l 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      perf-profile-level-0
+        ...
+
+        speed-select-base-freq:disabled
+       ...
+
+Before enabling Intel(R) SST-BF and measuring its impact on a workload
+performance, execute some workload and measure performance and get a baseline
+performance to compare against.
+
+Here the user wants more guaranteed performance. For this reason, it is likely
+that turbo is disabled. To disable turbo, execute::
+
+#echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
+
+Based on the output of the "intel-speed-select perf-profile info -l 0" base
+frequency of guaranteed frequency 2600 MHz.
+
+
+Measure baseline performance for comparison
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To compare, pick a multi-threaded workload where each thread can be scheduled on
+separate CPUs. "Hackbench pipe" test is a good example on how to improve
+performance using Intel(R) SST-BF.
+
+Below, the workload is measuring average scheduler wakeup latency, so a lower
+number means better performance::
+
+ # taskset -c 3,4 perf bench -r 100 sched pipe
+ # Running 'sched/pipe' benchmark:
+ # Executed 1000000 pipe operations between two processes
+     Total time: 6.102 [sec]
+       6.102445 usecs/op
+         163868 ops/sec
+
+While running the above test, if we take turbostat output, it will show us that
+2 of the CPUs are busy and reaching max. frequency (which would be the base
+frequency as the turbo is disabled). The turbostat output::
+
+ #turbostat -c 0-13 --show Package,Core,CPU,Bzy_MHz -i 1
+ Package       Core    CPU     Bzy_MHz
+ 0             0       0       1000
+ 0             1       1       1005
+ 0             2       2       1000
+ 0             3       3       2600
+ 0             4       4       2600
+ 0             5       5       1000
+ 0             6       6       1000
+ 0             7       7       1005
+ 0             8       8       1005
+ 0             9       9       1000
+ 0             10      10      1000
+ 0             11      11      995
+ 0             12      12      1000
+ 0             13      13      1000
+
+From the above turbostat output, both CPU 3 and 4 are very busy and reaching
+full guaranteed frequency of 2600 MHz.
+
+Intel(R) SST-BF Capabilities
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get capabilities of Intel(R) SST-BF for the current performance level 0,
+execute::
+
+ # intel-speed-select base-freq info -l 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      speed-select-base-freq
+        high-priority-base-frequency(MHz):3000
+        high-priority-cpu-mask:00000216,00002160
+        high-priority-cpu-list:5,6,8,13,33,34,36,41
+        low-priority-base-frequency(MHz):2400
+        tjunction-temperature(C):125
+        thermal-design-power(W):205
+
+The above capabilities show that there are some CPUs on this system that can
+offer base frequency of 3000 MHz compared to the standard base frequency at this
+performance levels. Nevertheless, these CPUs are fixed, and they are presented
+via high-priority-cpu-list/high-priority-cpu-mask. But if this Intel(R) SST-BF
+feature is selected, the low priorities CPUs (which are not in
+high-priority-cpu-list) can only offer up to 2400 MHz. As a result, if this
+clipping of low priority CPUs is acceptable, then the user can enable Intel
+SST-BF feature particularly for the above "sched pipe" workload since only two
+CPUs are used, they can be scheduled on high priority CPUs and can get boost of
+400 MHz.
+
+Enable Intel(R) SST-BF
+~~~~~~~~~~~~~~~~~~~~~~
+
+To enable Intel(R) SST-BF feature, execute::
+
+ # intel-speed-select base-freq enable -a
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      base-freq
+        enable:success
+ package-1
+  die-0
+    cpu-14
+      base-freq
+        enable:success
+
+In this case, -a option is optional. This not only enables Intel(R) SST-BF, but it
+also adjusts the priority of cores using Intel(R) Speed Select Technology Core
+Power (Intel(R) SST-CP) features. This option sets the minimum performance of each
+Intel(R) Speed Select Technology - Performance Profile (Intel(R) SST-PP) class to
+maximum performance so that the hardware will give maximum performance possible
+for each CPU.
+
+If -a option is not used, then the following steps are required before enabling
+Intel(R) SST-BF:
+
+- Discover Intel(R) SST-BF and note low and high priority base frequency
+- Note the high prioity CPU list
+- Enable CLOS using core-power feature set
+- Configure CLOS parameters. Use CLOS.min to set to minimum performance
+- Subscribe desired CPUs to CLOS groups
+
+With this configuration, if the same workload is executed by pinning the
+workload to high priority CPUs (CPU 5 and 6 in this case)::
+
+ #taskset -c 5,6 perf bench -r 100 sched pipe
+ # Running 'sched/pipe' benchmark:
+ # Executed 1000000 pipe operations between two processes
+     Total time: 5.627 [sec]
+       5.627922 usecs/op
+         177685 ops/sec
+
+This way, by enabling Intel(R) SST-BF, the performance of this benchmark is
+improved (latency reduced) by 7.79%. From the turbostat output, it can be
+observed that the high priority CPUs reached 3000 MHz compared to 2600 MHz.
+The turbostat output::
+
+ #turbostat -c 0-13 --show Package,Core,CPU,Bzy_MHz -i 1
+ Package       Core    CPU     Bzy_MHz
+ 0             0       0       2151
+ 0             1       1       2166
+ 0             2       2       2175
+ 0             3       3       2175
+ 0             4       4       2175
+ 0             5       5       3000
+ 0             6       6       3000
+ 0             7       7       2180
+ 0             8       8       2662
+ 0             9       9       2176
+ 0             10      10      2175
+ 0             11      11      2176
+ 0             12      12      2176
+ 0             13      13      2661
+
+Disable Intel(R) SST-BF
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To disable the Intel(R) SST-BF feature, execute::
+
+# intel-speed-select base-freq disable -a
+
+
+Intel(R) Speed Select Technology - Turbo Frequency (Intel(R) SST-TF)
+--------------------------------------------------------------------
+
+This feature enables the ability to set different "All core turbo ratio limits"
+to cores based on the priority. By using this feature, some cores can be
+configured to get higher turbo frequency by designating them as high priority at
+the cost of lower or no turbo frequency on the low priority cores.
+
+For this reason, this feature is only useful when system is busy utilizing all
+CPUs, but the user wants some configurable option to get high performance on
+some CPUs.
+
+The support of Intel(R) Speed Select Technology - Turbo Frequency (Intel(R) SST-TF)
+depends on the Intel(R) Speed Select Technology - Performance Profile (Intel
+SST-PP) performance level configuration. It is possible that only a certain
+performance level supports Intel(R) SST-TF. It is also possible that only the base
+performance level (level = 0) has the support of Intel(R) SST-TF. Hence, first
+select the desired performance level to enable this feature.
+
+In the system under test here, Intel(R) SST-TF is supported at the base
+performance level 0, but currently disabled::
+
+ # intel-speed-select -c 0 perf-profile info -l 0
+ Intel(R) Speed Select Technology
+ package-0
+  die-0
+    cpu-0
+      perf-profile-level-0
+        ...
+        ...
+        speed-select-turbo-freq:disabled
+        ...
+        ...
+
+
+To check if performance can be improved using Intel(R) SST-TF feature, get the turbo
+frequency properties with Intel(R) SST-TF enabled and compare to the base turbo
+capability of this system.
+
+Get Base turbo capability
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the base turbo capability of performance level 0, execute::
+
+ # intel-speed-select perf-profile info -l 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      perf-profile-level-0
+        ...
+        ...
+        turbo-ratio-limits-sse
+          bucket-0
+            core-count:2
+            max-turbo-frequency(MHz):3200
+          bucket-1
+            core-count:4
+            max-turbo-frequency(MHz):3100
+          bucket-2
+            core-count:6
+            max-turbo-frequency(MHz):3100
+          bucket-3
+            core-count:8
+            max-turbo-frequency(MHz):3100
+          bucket-4
+            core-count:10
+            max-turbo-frequency(MHz):3100
+          bucket-5
+            core-count:12
+            max-turbo-frequency(MHz):3100
+          bucket-6
+            core-count:14
+            max-turbo-frequency(MHz):3100
+          bucket-7
+            core-count:16
+            max-turbo-frequency(MHz):3100
+
+Based on the data above, when all the CPUS are busy, the max. frequency of 3100
+MHz can be achieved. If there is some busy workload on cpu 0 - 11 (e.g. stress)
+and on CPU 12 and 13, execute "hackbench pipe" workload::
+
+ # taskset -c 12,13 perf bench -r 100 sched pipe
+ # Running 'sched/pipe' benchmark:
+ # Executed 1000000 pipe operations between two processes
+     Total time: 5.705 [sec]
+       5.705488 usecs/op
+         175269 ops/sec
+
+The turbostat output::
+
+ #turbostat -c 0-13 --show Package,Core,CPU,Bzy_MHz -i 1
+ Package       Core    CPU     Bzy_MHz
+ 0             0       0       3000
+ 0             1       1       3000
+ 0             2       2       3000
+ 0             3       3       3000
+ 0             4       4       3000
+ 0             5       5       3100
+ 0             6       6       3100
+ 0             7       7       3000
+ 0             8       8       3100
+ 0             9       9       3000
+ 0             10      10      3000
+ 0             11      11      3000
+ 0             12      12      3100
+ 0             13      13      3100
+
+Based on turbostat output, the performance is limited by frequency cap of 3100
+MHz. To check if the hackbench performance can be improved for CPU 12 and CPU
+13, first check the capability of the Intel(R) SST-TF feature for this performance
+level.
+
+Get Intel(R) SST-TF Capability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To get the capability, the "turbo-freq info" command can be used::
+
+ # intel-speed-select turbo-freq info -l 0
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-0
+      speed-select-turbo-freq
+          bucket-0
+            high-priority-cores-count:2
+            high-priority-max-frequency(MHz):3200
+            high-priority-max-avx2-frequency(MHz):3200
+            high-priority-max-avx512-frequency(MHz):3100
+          bucket-1
+            high-priority-cores-count:4
+            high-priority-max-frequency(MHz):3100
+            high-priority-max-avx2-frequency(MHz):3000
+            high-priority-max-avx512-frequency(MHz):2900
+          bucket-2
+            high-priority-cores-count:6
+            high-priority-max-frequency(MHz):3100
+            high-priority-max-avx2-frequency(MHz):3000
+            high-priority-max-avx512-frequency(MHz):2900
+          speed-select-turbo-freq-clip-frequencies
+            low-priority-max-frequency(MHz):2600
+            low-priority-max-avx2-frequency(MHz):2400
+            low-priority-max-avx512-frequency(MHz):2100
+
+Based on the output above, there is an Intel(R) SST-TF bucket for which there are
+two high priority cores. If only two high priority cores are set, then max.
+turbo frequency on those cores can be increased to 3200 MHz. This is 100 MHz
+more than the base turbo capability for all cores.
+
+In turn, for the hackbench workload, two CPUs can be set as high priority and
+rest as low priority. One side effect is that once enabled, the low priority
+cores will be clipped to a lower frequency of 2600 MHz.
+
+Enable Intel(R) SST-TF
+~~~~~~~~~~~~~~~~~~~~~~
+
+To enable Intel(R) SST-TF, execute::
+
+ # intel-speed-select -c 12,13 turbo-freq enable -a
+ Intel(R) Speed Select Technology
+ Executing on CPU model: X
+ package-0
+  die-0
+    cpu-12
+      turbo-freq
+        enable:success
+ package-0
+  die-0
+    cpu-13
+      turbo-freq
+        enable:success
+ package--1
+  die-0
+    cpu-63
+      turbo-freq --auto
+        enable:success
+
+In this case, the option "-a" is optional. If set, it enables Intel(R) SST-TF
+feature and also sets the CPUs to high and and low priority using Intel Speed
+Select Technology Core Power (Intel(R) SST-CP) features. The CPU numbers passed
+with "-c" arguments are marked as high priority, including its siblings.
+
+If -a option is not used, then the following steps are required before enabling
+Intel(R) SST-TF:
+
+- Discover Intel(R) SST-TF and note buckets of high priority cores and maximum frequency
+
+- Enable CLOS using core-power feature set - Configure CLOS parameters
+
+- Subscribe desired CPUs to CLOS groups making sure that high priority cores are set to the maximum frequency
+
+If the same hackbench workload is executed, schedule hackbench threads on high
+priority CPUs::
+
+ #taskset -c 12,13 perf bench -r 100 sched pipe
+ # Running 'sched/pipe' benchmark:
+ # Executed 1000000 pipe operations between two processes
+     Total time: 5.510 [sec]
+       5.510165 usecs/op
+         180826 ops/sec
+
+This improved performance by around 3.3% improvement on a busy system. Here the
+turbostat output will show that the CPU 12 and CPU 13 are getting 100 MHz boost.
+The turbostat output::
+
+ #turbostat -c 0-13 --show Package,Core,CPU,Bzy_MHz -i 1
+ Package       Core    CPU     Bzy_MHz
+ ...
+ 0             12      12      3200
+ 0             13      13      3200
index ad392f3aee06149f12a4e6554fec7efedc568ede..39d80bc29ccd6e11c13ca084d34343b6238456ca 100644 (file)
@@ -62,9 +62,10 @@ on the capabilities of the processor.
 Active Mode
 -----------
 
-This is the default operation mode of ``intel_pstate``.  If it works in this
-mode, the ``scaling_driver`` policy attribute in ``sysfs`` for all ``CPUFreq``
-policies contains the string "intel_pstate".
+This is the default operation mode of ``intel_pstate`` for processors with
+hardware-managed P-states (HWP) support.  If it works in this mode, the
+``scaling_driver`` policy attribute in ``sysfs`` for all ``CPUFreq`` policies
+contains the string "intel_pstate".
 
 In this mode the driver bypasses the scaling governors layer of ``CPUFreq`` and
 provides its own scaling algorithms for P-state selection.  Those algorithms
@@ -138,12 +139,13 @@ internal P-state selection logic to be less performance-focused.
 Active Mode Without HWP
 ~~~~~~~~~~~~~~~~~~~~~~~
 
-This is the default operation mode for processors that do not support the HWP
-feature.  It also is used by default with the ``intel_pstate=no_hwp`` argument
-in the kernel command line.  However, in this mode ``intel_pstate`` may refuse
-to work with the given processor if it does not recognize it.  [Note that
-``intel_pstate`` will never refuse to work with any processor with the HWP
-feature enabled.]
+This operation mode is optional for processors that do not support the HWP
+feature or when the ``intel_pstate=no_hwp`` argument is passed to the kernel in
+the command line.  The active mode is used in those cases if the
+``intel_pstate=active`` argument is passed to the kernel in the command line.
+In this mode ``intel_pstate`` may refuse to work with processors that are not
+recognized by it.  [Note that ``intel_pstate`` will never refuse to work with
+any processor with the HWP feature enabled.]
 
 In this mode ``intel_pstate`` registers utilization update callbacks with the
 CPU scheduler in order to run a P-state selection algorithm, either
@@ -188,10 +190,14 @@ is not set.
 Passive Mode
 ------------
 
-This mode is used if the ``intel_pstate=passive`` argument is passed to the
-kernel in the command line (it implies the ``intel_pstate=no_hwp`` setting too).
-Like in the active mode without HWP support, in this mode ``intel_pstate`` may
-refuse to work with the given processor if it does not recognize it.
+This is the default operation mode of ``intel_pstate`` for processors without
+hardware-managed P-states (HWP) support.  It is always used if the
+``intel_pstate=passive`` argument is passed to the kernel in the command line
+regardless of whether or not the given processor supports HWP.  [Note that the
+``intel_pstate=no_hwp`` setting implies ``intel_pstate=passive`` if it is used
+without ``intel_pstate=active``.]  Like in the active mode without HWP support,
+in this mode ``intel_pstate`` may refuse to work with processors that are not
+recognized by it.
 
 If the driver works in this mode, the ``scaling_driver`` policy attribute in
 ``sysfs`` for all ``CPUFreq`` policies contains the string "intel_cpufreq".
index 0a38cdf39df1ed95cdae43ad7870585b636d5ee3..f40994c422dc0099ed5dd2730f3cd611e075faa2 100644 (file)
@@ -13,3 +13,4 @@ Working-State Power Management
    intel_pstate
    cpufreq_drivers
    intel_epb
+   intel-speed-select
diff --git a/Documentation/admin-guide/pstore-blk.rst b/Documentation/admin-guide/pstore-blk.rst
new file mode 100644 (file)
index 0000000..296d502
--- /dev/null
@@ -0,0 +1,243 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+pstore block oops/panic logger
+==============================
+
+Introduction
+------------
+
+pstore block (pstore/blk) is an oops/panic logger that writes its logs to a
+block device and non-block device before the system crashes. You can get
+these log files by mounting pstore filesystem like::
+
+    mount -t pstore pstore /sys/fs/pstore
+
+
+pstore block concepts
+---------------------
+
+pstore/blk provides efficient configuration method for pstore/blk, which
+divides all configurations into two parts, configurations for user and
+configurations for driver.
+
+Configurations for user determine how pstore/blk works, such as pmsg_size,
+kmsg_size and so on. All of them support both Kconfig and module parameters,
+but module parameters have priority over Kconfig.
+
+Configurations for driver are all about block device and non-block device,
+such as total_size of block device and read/write operations.
+
+Configurations for user
+-----------------------
+
+All of these configurations support both Kconfig and module parameters, but
+module parameters have priority over Kconfig.
+
+Here is an example for module parameters::
+
+        pstore_blk.blkdev=179:7 pstore_blk.kmsg_size=64
+
+The detail of each configurations may be of interest to you.
+
+blkdev
+~~~~~~
+
+The block device to use. Most of the time, it is a partition of block device.
+It's required for pstore/blk. It is also used for MTD device.
+
+It accepts the following variants for block device:
+
+1. <hex_major><hex_minor> device number in hexadecimal represents itself; no
+   leading 0x, for example b302.
+#. /dev/<disk_name> represents the device number of disk
+#. /dev/<disk_name><decimal> represents the device number of partition - device
+   number of disk plus the partition number
+#. /dev/<disk_name>p<decimal> - same as the above; this form is used when disk
+   name of partitioned disk ends with a digit.
+#. PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF represents the unique id of
+   a partition if the partition table provides it. The UUID may be either an
+   EFI/GPT UUID, or refer to an MSDOS partition using the format SSSSSSSS-PP,
+   where SSSSSSSS is a zero-filled hex representation of the 32-bit
+   "NT disk signature", and PP is a zero-filled hex representation of the
+   1-based partition number.
+#. PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to a
+   partition with a known unique id.
+#. <major>:<minor> major and minor number of the device separated by a colon.
+
+It accepts the following variants for MTD device:
+
+1. <device name> MTD device name. "pstore" is recommended.
+#. <device number> MTD device number.
+
+kmsg_size
+~~~~~~~~~
+
+The chunk size in KB for oops/panic front-end. It **MUST** be a multiple of 4.
+It's optional if you do not care oops/panic log.
+
+There are multiple chunks for oops/panic front-end depending on the remaining
+space except other pstore front-ends.
+
+pstore/blk will log to oops/panic chunks one by one, and always overwrite the
+oldest chunk if there is no more free chunk.
+
+pmsg_size
+~~~~~~~~~
+
+The chunk size in KB for pmsg front-end. It **MUST** be a multiple of 4.
+It's optional if you do not care pmsg log.
+
+Unlike oops/panic front-end, there is only one chunk for pmsg front-end.
+
+Pmsg is a user space accessible pstore object. Writes to */dev/pmsg0* are
+appended to the chunk. On reboot the contents are available in
+*/sys/fs/pstore/pmsg-pstore-blk-0*.
+
+console_size
+~~~~~~~~~~~~
+
+The chunk size in KB for console front-end.  It **MUST** be a multiple of 4.
+It's optional if you do not care console log.
+
+Similar to pmsg front-end, there is only one chunk for console front-end.
+
+All log of console will be appended to the chunk. On reboot the contents are
+available in */sys/fs/pstore/console-pstore-blk-0*.
+
+ftrace_size
+~~~~~~~~~~~
+
+The chunk size in KB for ftrace front-end. It **MUST** be a multiple of 4.
+It's optional if you do not care console log.
+
+Similar to oops front-end, there are multiple chunks for ftrace front-end
+depending on the count of cpu processors. Each chunk size is equal to
+ftrace_size / processors_count.
+
+All log of ftrace will be appended to the chunk. On reboot the contents are
+combined and available in */sys/fs/pstore/ftrace-pstore-blk-0*.
+
+Persistent function tracing might be useful for debugging software or hardware
+related hangs. Here is an example of usage::
+
+ # mount -t pstore pstore /sys/fs/pstore
+ # mount -t debugfs debugfs /sys/kernel/debug/
+ # echo 1 > /sys/kernel/debug/pstore/record_ftrace
+ # reboot -f
+ [...]
+ # mount -t pstore pstore /sys/fs/pstore
+ # tail /sys/fs/pstore/ftrace-pstore-blk-0
+ CPU:0 ts:5914676 c0063828  c0063b94  call_cpuidle <- cpu_startup_entry+0x1b8/0x1e0
+ CPU:0 ts:5914678 c039ecdc  c006385c  cpuidle_enter_state <- call_cpuidle+0x44/0x48
+ CPU:0 ts:5914680 c039e9a0  c039ecf0  cpuidle_enter_freeze <- cpuidle_enter_state+0x304/0x314
+ CPU:0 ts:5914681 c0063870  c039ea30  sched_idle_set_state <- cpuidle_enter_state+0x44/0x314
+ CPU:1 ts:5916720 c0160f59  c015ee04  kernfs_unmap_bin_file <- __kernfs_remove+0x140/0x204
+ CPU:1 ts:5916721 c05ca625  c015ee0c  __mutex_lock_slowpath <- __kernfs_remove+0x148/0x204
+ CPU:1 ts:5916723 c05c813d  c05ca630  yield_to <- __mutex_lock_slowpath+0x314/0x358
+ CPU:1 ts:5916724 c05ca2d1  c05ca638  __ww_mutex_lock <- __mutex_lock_slowpath+0x31c/0x358
+
+max_reason
+~~~~~~~~~~
+
+Limiting which kinds of kmsg dumps are stored can be controlled via
+the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
+``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
+``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
+``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
+(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
+``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
+otherwise KMSG_DUMP_MAX.
+
+Configurations for driver
+-------------------------
+
+Only a block device driver cares about these configurations. A block device
+driver uses ``register_pstore_blk`` to register to pstore/blk.
+
+.. kernel-doc:: fs/pstore/blk.c
+   :identifiers: register_pstore_blk
+
+A non-block device driver uses ``register_pstore_device`` with
+``struct pstore_device_info`` to register to pstore/blk.
+
+.. kernel-doc:: fs/pstore/blk.c
+   :identifiers: register_pstore_device
+
+.. kernel-doc:: include/linux/pstore_blk.h
+   :identifiers: pstore_device_info
+
+Compression and header
+----------------------
+
+Block device is large enough for uncompressed oops data. Actually we do not
+recommend data compression because pstore/blk will insert some information into
+the first line of oops/panic data. For example::
+
+        Panic: Total 16 times
+
+It means that it's OOPS|Panic for the 16th time since the first booting.
+Sometimes the number of occurrences of oops|panic since the first booting is
+important to judge whether the system is stable.
+
+The following line is inserted by pstore filesystem. For example::
+
+        Oops#2 Part1
+
+It means that it's OOPS for the 2nd time on the last boot.
+
+Reading the data
+----------------
+
+The dump data can be read from the pstore filesystem. The format for these
+files is ``dmesg-pstore-blk-[N]`` for oops/panic front-end,
+``pmsg-pstore-blk-0`` for pmsg front-end and so on.  The timestamp of the
+dump file records the trigger time. To delete a stored record from block
+device, simply unlink the respective pstore file.
+
+Attentions in panic read/write APIs
+-----------------------------------
+
+If on panic, the kernel is not going to run for much longer, the tasks will not
+be scheduled and most kernel resources will be out of service. It
+looks like a single-threaded program running on a single-core computer.
+
+The following points require special attention for panic read/write APIs:
+
+1. Can **NOT** allocate any memory.
+   If you need memory, just allocate while the block driver is initializing
+   rather than waiting until the panic.
+#. Must be polled, **NOT** interrupt driven.
+   No task schedule any more. The block driver should delay to ensure the write
+   succeeds, but NOT sleep.
+#. Can **NOT** take any lock.
+   There is no other task, nor any shared resource; you are safe to break all
+   locks.
+#. Just use CPU to transfer.
+   Do not use DMA to transfer unless you are sure that DMA will not keep lock.
+#. Control registers directly.
+   Please control registers directly rather than use Linux kernel resources.
+   Do I/O map while initializing rather than wait until a panic occurs.
+#. Reset your block device and controller if necessary.
+   If you are not sure of the state of your block device and controller when
+   a panic occurs, you are safe to stop and reset them.
+
+pstore/blk supports psblk_blkdev_info(), which is defined in
+*linux/pstore_blk.h*, to get information of using block device, such as the
+device number, sector count and start sector of the whole disk.
+
+pstore block internals
+----------------------
+
+For developer reference, here are all the important structures and APIs:
+
+.. kernel-doc:: fs/pstore/zone.c
+   :internal:
+
+.. kernel-doc:: include/linux/pstore_zone.h
+   :internal:
+
+.. kernel-doc:: fs/pstore/blk.c
+   :export:
+
+.. kernel-doc:: include/linux/pstore_blk.h
+   :internal:
index 6dbcc5481000a1c5ec3fb5864cf2d188985eeb8f..a60a96218ba9032ca11667859d1a9d4d35771e10 100644 (file)
@@ -32,11 +32,17 @@ memory to be mapped strongly ordered, and atomic operations on strongly ordered
 memory are implementation defined, and won't work on many ARMs such as omaps.
 
 The memory area is divided into ``record_size`` chunks (also rounded down to
-power of two) and each oops/panic writes a ``record_size`` chunk of
+power of two) and each kmesg dump writes a ``record_size`` chunk of
 information.
 
-Dumping both oopses and panics can be done by setting 1 in the ``dump_oops``
-variable while setting 0 in that variable dumps only the panics.
+Limiting which kinds of kmsg dumps are stored can be controlled via
+the ``max_reason`` value, as defined in include/linux/kmsg_dump.h's
+``enum kmsg_dump_reason``. For example, to store both Oopses and Panics,
+``max_reason`` should be set to 2 (KMSG_DUMP_OOPS), to store only Panics
+``max_reason`` should be set to 1 (KMSG_DUMP_PANIC). Setting this to 0
+(KMSG_DUMP_UNDEF), means the reason filtering will be controlled by the
+``printk.always_kmsg_dump`` boot param: if unset, it'll be KMSG_DUMP_OOPS,
+otherwise KMSG_DUMP_MAX.
 
 The module uses a counter to record multiple dumps but the counter gets reset
 on restart (i.e. new dumps after the restart will overwrite old ones).
@@ -90,7 +96,7 @@ Setting the ramoops parameters can be done in several different manners:
         .mem_address            = <...>,
         .mem_type               = <...>,
         .record_size            = <...>,
-        .dump_oops              = <...>,
+        .max_reason             = <...>,
         .ecc                    = <...>,
   };
 
index 0310db624964cc41b99204ecedc06795a19bc16b..7b481b2a368e7cbca173a665f339577dae1133f9 100644 (file)
@@ -156,11 +156,11 @@ the labels provided by the BIOS won't match the real ones.
 ECC memory
 ----------
 
-As mentioned on the previous section, ECC memory has extra bits to be
-used for error correction. So, on 64 bit systems, a memory module
-has 64 bits of *data width*, and 74 bits of *total width*. So, there are
-8 bits extra bits to be used for the error detection and correction
-mechanisms. Those extra bits are called *syndrome*\ [#f1]_\ [#f2]_.
+As mentioned in the previous section, ECC memory has extra bits to be
+used for error correction. In the above example, a memory module has
+64 bits of *data width*, and 72 bits of *total width*.  The extra 8
+bits which are used for the error detection and correction mechanisms
+are referred to as the *syndrome*\ [#f1]_\ [#f2]_.
 
 So, when the cpu requests the memory controller to write a word with
 *data width*, the memory controller calculates the *syndrome* in real time,
@@ -212,7 +212,7 @@ EDAC - Error Detection And Correction
    purposes.
 
    When the subsystem was pushed upstream for the first time, on
-   Kernel 2.6.16, for the first time, it was renamed to ``EDAC``.
+   Kernel 2.6.16, it was renamed to ``EDAC``.
 
 Purpose
 -------
@@ -351,15 +351,17 @@ controllers. The following example will assume 2 channels:
        +------------+-----------+-----------+
        |            |  ``ch0``  |  ``ch1``  |
        +============+===========+===========+
-       | ``csrow0`` |  DIMM_A0  |  DIMM_B0  |
-       |            |   rank0   |   rank0   |
-       +------------+     -     |     -     |
+       |            |**DIMM_A0**|**DIMM_B0**|
+       +------------+-----------+-----------+
+       | ``csrow0`` |   rank0   |   rank0   |
+       +------------+-----------+-----------+
        | ``csrow1`` |   rank1   |   rank1   |
        +------------+-----------+-----------+
-       | ``csrow2`` |  DIMM_A1  | DIMM_B1   |
-       |            |   rank0   |   rank0   |
-       +------------+     -     |     -     |
-       | ``csrow3`` |   rank1   |   rank1   |
+       |            |**DIMM_A1**|**DIMM_B1**|
+       +------------+-----------+-----------+
+       | ``csrow2`` |    rank0  |  rank0    |
+       +------------+-----------+-----------+
+       | ``csrow3`` |    rank1  |  rank1    |
        +------------+-----------+-----------+
 
 In the above example, there are 4 physical slots on the motherboard
index 0d427fd1094194204d0fed72caf4b468e2d62017..1ebf68d0114110c29b9a62c35864667d5668a431 100644 (file)
@@ -102,6 +102,30 @@ See the ``type_of_loader`` and ``ext_loader_ver`` fields in
 :doc:`/x86/boot` for additional information.
 
 
+bpf_stats_enabled
+=================
+
+Controls whether the kernel should collect statistics on BPF programs
+(total time spent running, number of times run...). Enabling
+statistics causes a slight reduction in performance on each program
+run. The statistics can be seen using ``bpftool``.
+
+= ===================================
+0 Don't collect statistics (default).
+1 Collect statistics.
+= ===================================
+
+
+cad_pid
+=======
+
+This is the pid which will be signalled on reboot (notably, by
+Ctrl-Alt-Delete). Writing a value to this file which doesn't
+correspond to a running process will result in ``-ESRCH``.
+
+See also `ctrl-alt-del`_.
+
+
 cap_last_cap
 ============
 
@@ -241,6 +265,40 @@ domain names are in general different. For a detailed discussion
 see the ``hostname(1)`` man page.
 
 
+firmware_config
+===============
+
+See :doc:`/driver-api/firmware/fallback-mechanisms`.
+
+The entries in this directory allow the firmware loader helper
+fallback to be controlled:
+
+* ``force_sysfs_fallback``, when set to 1, forces the use of the
+  fallback;
+* ``ignore_sysfs_fallback``, when set to 1, ignores any fallback.
+
+
+ftrace_dump_on_oops
+===================
+
+Determines whether ``ftrace_dump()`` should be called on an oops (or
+kernel panic). This will output the contents of the ftrace buffers to
+the console.  This is very useful for capturing traces that lead to
+crashes and outputting them to a serial console.
+
+= ===================================================
+0 Disabled (default).
+1 Dump buffers of all CPUs.
+2 Dump the buffer of the CPU that triggered the oops.
+= ===================================================
+
+
+ftrace_enabled, stack_tracer_enabled
+====================================
+
+See :doc:`/trace/ftrace`.
+
+
 hardlockup_all_cpu_backtrace
 ============================
 
@@ -344,6 +402,25 @@ Controls whether the panic kmsg data should be reported to Hyper-V.
 = =========================================================
 
 
+ignore-unaligned-usertrap
+=========================
+
+On architectures where unaligned accesses cause traps, and where this
+feature is supported (``CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN``;
+currently, ``arc`` and ``ia64``), controls whether all unaligned traps
+are logged.
+
+= =============================================================
+0 Log all unaligned accesses.
+1 Only warn the first time a process traps. This is the default
+  setting.
+= =============================================================
+
+See also `unaligned-trap`_ and `unaligned-dump-stack`_. On ``ia64``,
+this allows system administrators to override the
+``IA64_THREAD_UAC_NOPRINT`` ``prctl`` and avoid logs being flooded.
+
+
 kexec_load_disabled
 ===================
 
@@ -459,6 +536,15 @@ Notes:
      successful IPC object allocation. If an IPC object allocation syscall
      fails, it is undefined if the value remains unmodified or is reset to -1.
 
+
+ngroups_max
+===========
+
+Maximum number of supplementary groups, _i.e._ the maximum size which
+``setgroups`` will accept. Exports ``NGROUPS_MAX`` from the kernel.
+
+
+
 nmi_watchdog
 ============
 
@@ -721,7 +807,13 @@ perf_event_paranoid
 ===================
 
 Controls use of the performance events system by unprivileged
-users (without CAP_SYS_ADMIN).  The default value is 2.
+users (without CAP_PERFMON).  The default value is 2.
+
+For backward compatibility reasons access to system performance
+monitoring and observability remains open for CAP_SYS_ADMIN
+privileged processes but CAP_SYS_ADMIN usage for secure system
+performance monitoring and observability operations is discouraged
+with respect to CAP_PERFMON use cases.
 
 ===  ==================================================================
  -1  Allow use of (almost) all events by all users.
@@ -730,13 +822,13 @@ users (without CAP_SYS_ADMIN).  The default value is 2.
      ``CAP_IPC_LOCK``.
 
 >=0  Disallow ftrace function tracepoint by users without
-     ``CAP_SYS_ADMIN``.
+     ``CAP_PERFMON``.
 
-     Disallow raw tracepoint access by users without ``CAP_SYS_ADMIN``.
+     Disallow raw tracepoint access by users without ``CAP_PERFMON``.
 
->=1  Disallow CPU event access by users without ``CAP_SYS_ADMIN``.
+>=1  Disallow CPU event access by users without ``CAP_PERFMON``.
 
->=2  Disallow kernel profiling by users without ``CAP_SYS_ADMIN``.
+>=2  Disallow kernel profiling by users without ``CAP_PERFMON``.
 ===  ==================================================================
 
 
@@ -871,7 +963,7 @@ this sysctl interface anymore.
 pty
 ===
 
-See Documentation/filesystems/devpts.txt.
+See Documentation/filesystems/devpts.rst.
 
 
 randomize_va_space
@@ -1167,6 +1259,65 @@ If a value outside of this range is written to ``threads-max`` an
 ``EINVAL`` error occurs.
 
 
+traceoff_on_warning
+===================
+
+When set, disables tracing (see :doc:`/trace/ftrace`) when a
+``WARN()`` is hit.
+
+
+tracepoint_printk
+=================
+
+When tracepoints are sent to printk() (enabled by the ``tp_printk``
+boot parameter), this entry provides runtime control::
+
+    echo 0 > /proc/sys/kernel/tracepoint_printk
+
+will stop tracepoints from being sent to printk(), and::
+
+    echo 1 > /proc/sys/kernel/tracepoint_printk
+
+will send them to printk() again.
+
+This only works if the kernel was booted with ``tp_printk`` enabled.
+
+See :doc:`/admin-guide/kernel-parameters` and
+:doc:`/trace/boottime-trace`.
+
+
+.. _unaligned-dump-stack:
+
+unaligned-dump-stack (ia64)
+===========================
+
+When logging unaligned accesses, controls whether the stack is
+dumped.
+
+= ===================================================
+0 Do not dump the stack. This is the default setting.
+1 Dump the stack.
+= ===================================================
+
+See also `ignore-unaligned-usertrap`_.
+
+
+unaligned-trap
+==============
+
+On architectures where unaligned accesses cause traps, and where this
+feature is supported (``CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW``; currently,
+``arc`` and ``parisc``), controls whether unaligned traps are caught
+and emulated (instead of failing).
+
+= ========================================================
+0 Do not emulate unaligned accesses.
+1 Emulate unaligned accesses. This is the default setting.
+= ========================================================
+
+See also `ignore-unaligned-usertrap`_.
+
+
 unknown_nmi_panic
 =================
 
@@ -1178,6 +1329,16 @@ NMI switch that most IA32 servers have fires unknown NMI up, for
 example.  If a system hangs up, try pressing the NMI switch.
 
 
+unprivileged_bpf_disabled
+=========================
+
+Writing 1 to this entry will disable unprivileged calls to ``bpf()``;
+once disabled, calling ``bpf()`` without ``CAP_SYS_ADMIN`` will return
+``-EPERM``.
+
+Once set, this can't be cleared.
+
+
 watchdog
 ========
 
index 036783ee327f252726f645d4684b6673f8a4aad9..452ec8b115c278b225c0c77cd36ebe8fd5f0fdf8 100644 (file)
@@ -24,13 +24,13 @@ optional external memory-mapped interface.
 Version 1 of the Activity Monitors architecture implements a counter group
 of four fixed and architecturally defined 64-bit event counters.
 
-- CPU cycle counter: increments at the frequency of the CPU.
-- Constant counter: increments at the fixed frequency of the system
-  clock.
-- Instructions retired: increments with every architecturally executed
-  instruction.
-- Memory stall cycles: counts instruction dispatch stall cycles caused by
-  misses in the last level cache within the clock domain.
+  - CPU cycle counter: increments at the frequency of the CPU.
+  - Constant counter: increments at the fixed frequency of the system
+    clock.
+  - Instructions retired: increments with every architecturally executed
+    instruction.
+  - Memory stall cycles: counts instruction dispatch stall cycles caused by
+    misses in the last level cache within the clock domain.
 
 When in WFI or WFE these counters do not increment.
 
@@ -59,11 +59,11 @@ counters, only the presence of the extension.
 Firmware (code running at higher exception levels, e.g. arm-tf) support is
 needed to:
 
-- Enable access for lower exception levels (EL2 and EL1) to the AMU
-  registers.
-- Enable the counters. If not enabled these will read as 0.
-- Save/restore the counters before/after the CPU is being put/brought up
-  from the 'off' power state.
+ - Enable access for lower exception levels (EL2 and EL1) to the AMU
+   registers.
+ - Enable the counters. If not enabled these will read as 0.
+ - Save/restore the counters before/after the CPU is being put/brought up
+   from the 'off' power state.
 
 When using kernels that have this feature enabled but boot with broken
 firmware the user may experience panics or lockups when accessing the
@@ -81,10 +81,10 @@ are not trapped in EL2/EL3.
 The fixed counters of AMUv1 are accessible though the following system
 register definitions:
 
-- SYS_AMEVCNTR0_CORE_EL0
-- SYS_AMEVCNTR0_CONST_EL0
-- SYS_AMEVCNTR0_INST_RET_EL0
-- SYS_AMEVCNTR0_MEM_STALL_EL0
+ - SYS_AMEVCNTR0_CORE_EL0
+ - SYS_AMEVCNTR0_CONST_EL0
+ - SYS_AMEVCNTR0_INST_RET_EL0
+ - SYS_AMEVCNTR0_MEM_STALL_EL0
 
 Auxiliary platform specific counters can be accessed using
 SYS_AMEVCNTR1_EL0(n), where n is a value between 0 and 15.
@@ -97,9 +97,9 @@ Userspace access
 
 Currently, access from userspace to the AMU registers is disabled due to:
 
-- Security reasons: they might expose information about code executed in
-  secure mode.
-- Purpose: AMU counters are intended for system management use.
+ - Security reasons: they might expose information about code executed in
+   secure mode.
+ - Purpose: AMU counters are intended for system management use.
 
 Also, the presence of the feature is not visible to userspace.
 
@@ -110,8 +110,8 @@ Virtualization
 Currently, access from userspace (EL0) and kernelspace (EL1) on the KVM
 guest side is disabled due to:
 
-- Security reasons: they might expose information about code executed
-  by other guests or the host.
+ - Security reasons: they might expose information about code executed
+   by other guests or the host.
 
 Any attempt to access the AMU registers will result in an UNDEFINED
 exception being injected into the guest.
index a3f1a47b6f1c54fa94954b456a2cda7bcbdc776b..7552dbc1cc54c76d9b6ea3b6983d9ee800453d84 100644 (file)
@@ -173,7 +173,10 @@ Before jumping into the kernel, the following conditions must be met:
 - Caches, MMUs
 
   The MMU must be off.
-  Instruction cache may be on or off.
+
+  The instruction cache may be on or off, and must not hold any stale
+  entries corresponding to the loaded kernel image.
+
   The address range corresponding to the loaded kernel image must be
   cleaned to the PoC. In the presence of a system cache or other
   coherent masters with caches enabled, this will typically require
@@ -238,6 +241,7 @@ Before jumping into the kernel, the following conditions must be met:
   - The DT or ACPI tables must describe a GICv2 interrupt controller.
 
   For CPUs with pointer authentication functionality:
+
   - If EL3 is present:
 
     - SCR_EL3.APK (bit 16) must be initialised to 0b1
@@ -249,18 +253,22 @@ Before jumping into the kernel, the following conditions must be met:
     - HCR_EL2.API (bit 41) must be initialised to 0b1
 
   For CPUs with Activity Monitors Unit v1 (AMUv1) extension present:
+
   - If EL3 is present:
-    CPTR_EL3.TAM (bit 30) must be initialised to 0b0
-    CPTR_EL2.TAM (bit 30) must be initialised to 0b0
-    AMCNTENSET0_EL0 must be initialised to 0b1111
-    AMCNTENSET1_EL0 must be initialised to a platform specific value
-    having 0b1 set for the corresponding bit for each of the auxiliary
-    counters present.
+
+    - CPTR_EL3.TAM (bit 30) must be initialised to 0b0
+    - CPTR_EL2.TAM (bit 30) must be initialised to 0b0
+    - AMCNTENSET0_EL0 must be initialised to 0b1111
+    - AMCNTENSET1_EL0 must be initialised to a platform specific value
+      having 0b1 set for the corresponding bit for each of the auxiliary
+      counters present.
+
   - If the kernel is entered at EL1:
-    AMCNTENSET0_EL0 must be initialised to 0b1111
-    AMCNTENSET1_EL0 must be initialised to a platform specific value
-    having 0b1 set for the corresponding bit for each of the auxiliary
-    counters present.
+
+    - AMCNTENSET0_EL0 must be initialised to 0b1111
+    - AMCNTENSET1_EL0 must be initialised to a platform specific value
+      having 0b1 set for the corresponding bit for each of the auxiliary
+      counters present.
 
 The requirements described above for CPU mode, caches, MMUs, architected
 timers, coherency and system registers apply to all CPUs.  All CPUs must
@@ -304,7 +312,8 @@ following manner:
   Documentation/devicetree/bindings/arm/psci.yaml.
 
 - Secondary CPU general-purpose register settings
-  x0 = 0 (reserved for future use)
-  x1 = 0 (reserved for future use)
-  x2 = 0 (reserved for future use)
-  x3 = 0 (reserved for future use)
+
+  - x0 = 0 (reserved for future use)
+  - x1 = 0 (reserved for future use)
+  - x2 = 0 (reserved for future use)
+  - x3 = 0 (reserved for future use)
index 41937a8091aaa2a9c2252fc3f98615a4bbb25436..314fa5bc2655da3b30fdde5c12e602ae2633ccc5 100644 (file)
@@ -176,6 +176,8 @@ infrastructure:
      +------------------------------+---------+---------+
      | SSBS                         | [7-4]   |    y    |
      +------------------------------+---------+---------+
+     | BT                           | [3-0]   |    y    |
+     +------------------------------+---------+---------+
 
 
   4) MIDR_EL1 - Main ID Register
index 7dfb97dfe416058f20ef3863c9185d9a071f46ec..84a9fd2d41b4700d00fe35e2a595156ae03c6668 100644 (file)
@@ -236,6 +236,11 @@ HWCAP2_RNG
 
     Functionality implied by ID_AA64ISAR0_EL1.RNDR == 0b0001.
 
+HWCAP2_BTI
+
+    Functionality implied by ID_AA64PFR0_EL1.BT == 0b0001.
+
+
 4. Unused AT_HWCAP bits
 -----------------------
 
index 2c08c628febdf3c7a17c2129047d9f53e246a779..936cf2a59ca4b3fdc361e845cc61042fafcb8c5b 100644 (file)
@@ -64,6 +64,10 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A53      | #843419         | ARM64_ERRATUM_843419        |
 +----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-A55      | #1024718        | ARM64_ERRATUM_1024718       |
++----------------+-----------------+-----------------+-----------------------------+
+| ARM            | Cortex-A55      | #1530923        | ARM64_ERRATUM_1530923       |
++----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A57      | #832075         | ARM64_ERRATUM_832075        |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A57      | #852523         | N/A                         |
@@ -78,8 +82,6 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A73      | #858921         | ARM64_ERRATUM_858921        |
 +----------------+-----------------+-----------------+-----------------------------+
-| ARM            | Cortex-A55      | #1024718        | ARM64_ERRATUM_1024718       |
-+----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A76      | #1188873,1418040| ARM64_ERRATUM_1418040       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A76      | #1165522        | ARM64_ERRATUM_1165522       |
@@ -88,8 +90,6 @@ stable kernels.
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Cortex-A76      | #1463225        | ARM64_ERRATUM_1463225       |
 +----------------+-----------------+-----------------+-----------------------------+
-| ARM            | Cortex-A55      | #1530923        | ARM64_ERRATUM_1530923       |
-+----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-N1     | #1188873,1418040| ARM64_ERRATUM_1418040       |
 +----------------+-----------------+-----------------+-----------------------------+
 | ARM            | Neoverse-N1     | #1349291        | N/A                         |
index 9ae8e9abf846a1ddb43946e8b55cfadb32178d83..f6a1bc07c4101f60578a7be8e9022f9f273c5cf0 100644 (file)
@@ -388,44 +388,6 @@ if major == 1 and minor < 6:
 #  author, documentclass [howto, manual, or own class]).
 # Sorted in alphabetical order
 latex_documents = [
-    ('admin-guide/index', 'linux-user.tex', 'Linux Kernel User Documentation',
-     'The kernel development community', 'manual'),
-    ('core-api/index', 'core-api.tex', 'The kernel core API manual',
-     'The kernel development community', 'manual'),
-    ('crypto/index', 'crypto-api.tex', 'Linux Kernel Crypto API manual',
-     'The kernel development community', 'manual'),
-    ('dev-tools/index', 'dev-tools.tex', 'Development tools for the Kernel',
-     'The kernel development community', 'manual'),
-    ('doc-guide/index', 'kernel-doc-guide.tex', 'Linux Kernel Documentation Guide',
-     'The kernel development community', 'manual'),
-    ('driver-api/index', 'driver-api.tex', 'The kernel driver API manual',
-     'The kernel development community', 'manual'),
-    ('filesystems/index', 'filesystems.tex', 'Linux Filesystems API',
-     'The kernel development community', 'manual'),
-    ('admin-guide/ext4', 'ext4-admin-guide.tex', 'ext4 Administration Guide',
-     'ext4 Community', 'manual'),
-    ('filesystems/ext4/index', 'ext4-data-structures.tex',
-     'ext4 Data Structures and Algorithms', 'ext4 Community', 'manual'),
-    ('gpu/index', 'gpu.tex', 'Linux GPU Driver Developer\'s Guide',
-     'The kernel development community', 'manual'),
-    ('input/index', 'linux-input.tex', 'The Linux input driver subsystem',
-     'The kernel development community', 'manual'),
-    ('kernel-hacking/index', 'kernel-hacking.tex', 'Unreliable Guide To Hacking The Linux Kernel',
-     'The kernel development community', 'manual'),
-    ('media/index', 'media.tex', 'Linux Media Subsystem Documentation',
-     'The kernel development community', 'manual'),
-    ('networking/index', 'networking.tex', 'Linux Networking Documentation',
-     'The kernel development community', 'manual'),
-    ('process/index', 'development-process.tex', 'Linux Kernel Development Documentation',
-     'The kernel development community', 'manual'),
-    ('security/index', 'security.tex', 'The kernel security subsystem manual',
-     'The kernel development community', 'manual'),
-    ('sh/index', 'sh.tex', 'SuperH architecture implementation manual',
-     'The kernel development community', 'manual'),
-    ('sound/index', 'sound.tex', 'Linux Sound Subsystem Documentation',
-     'The kernel development community', 'manual'),
-    ('userspace-api/index', 'userspace-api.tex', 'The Linux kernel user-space API guide',
-     'The kernel development community', 'manual'),
 ]
 
 # Add all other index files from Documentation/ subdirectories
index 93cb65d52720a0ef72b2ba527ef3135b579c113e..a1582cc79f0fdb1bc786df43c8ebacc30f6d37fc 100644 (file)
@@ -213,7 +213,7 @@ Here are the routines, one by one:
        there will be no entries in the cache for the kernel address
        space for virtual addresses in the range 'start' to 'end-1'.
 
-       The first of these two routines is invoked after map_vm_area()
+       The first of these two routines is invoked after map_kernel_range()
        has installed the page table entries.  The second is invoked
        before unmap_kernel_range() deletes the page table entries.
 
index 0897ad12c11917f53578ceea06e1e2788b88983e..15ab8611262720da67254faf431a4b8ce1b3366a 100644 (file)
@@ -18,6 +18,7 @@ it.
 
    kernel-api
    workqueue
+   printk-basics
    printk-formats
    symbol-namespaces
 
@@ -30,10 +31,12 @@ Library functionality that is used throughout the kernel.
    :maxdepth: 1
 
    kobject
+   kref
    assoc_array
    xarray
    idr
    circular-buffers
+   rbtree
    generic-radix-tree
    packing
    timekeeping
@@ -50,6 +53,7 @@ How Linux keeps everything from happening at the same time.  See
 
    atomic_ops
    refcount-vs-atomic
+   irq/index
    local_ops
    padata
    ../RCU/index
@@ -78,6 +82,10 @@ more memory-management documentation in :doc:`/vm/index`.
    :maxdepth: 1
 
    memory-allocation
+   dma-api
+   dma-api-howto
+   dma-attributes
+   dma-isa-lpc
    mm-api
    genalloc
    pin_user_pages
@@ -92,6 +100,7 @@ Interfaces for kernel debugging
 
    debug-objects
    tracepoint
+   debugging-via-ohci1394
 
 Everything else
 ===============
diff --git a/Documentation/core-api/irq/index.rst b/Documentation/core-api/irq/index.rst
new file mode 100644 (file)
index 0000000..0d65d11
--- /dev/null
@@ -0,0 +1,11 @@
+====
+IRQs
+====
+
+.. toctree::
+   :maxdepth: 1
+
+   concepts
+   irq-affinity
+   irq-domain
+   irqflags-tracing
similarity index 99%
rename from Documentation/IRQ-domain.txt
rename to Documentation/core-api/irq/irq-domain.rst
index 507775cce7532f52021527ce6d0d0df97ae5d918..096db12f32d54d9c6a957fad7a1db40ac36f803c 100644 (file)
@@ -263,7 +263,8 @@ needs to:
 Hierarchy irq_domain is in no way x86 specific, and is heavily used to
 support other architectures, such as ARM, ARM64 etc.
 
-=== Debugging ===
+Debugging
+=========
 
 Most of the internals of the IRQ subsystem are exposed in debugfs by
 turning CONFIG_GENERIC_IRQ_DEBUGFS on.
index 1f62d4d7d9660f962fea723c2641c8e7bc3e141c..e93dc8cf52dd4b1a0d5fd890d54ced8c1cd73f72 100644 (file)
@@ -80,11 +80,11 @@ what is the pointer to the containing structure?  You must avoid tricks
 (such as assuming that the kobject is at the beginning of the structure)
 and, instead, use the container_of() macro, found in ``<linux/kernel.h>``::
 
-    container_of(pointer, type, member)
+    container_of(ptr, type, member)
 
 where:
 
-  * ``pointer`` is the pointer to the embedded kobject,
+  * ``ptr`` is the pointer to the embedded kobject,
   * ``type`` is the type of the containing structure, and
   * ``member`` is the name of the structure field to which ``pointer`` points.
 
@@ -140,7 +140,7 @@ the name of the kobject, call kobject_rename()::
 
     int kobject_rename(struct kobject *kobj, const char *new_name);
 
-kobject_rename does not perform any locking or have a solid notion of
+kobject_rename() does not perform any locking or have a solid notion of
 what names are valid so the caller must provide their own sanity checking
 and serialization.
 
@@ -210,7 +210,7 @@ statically and will warn the developer of this improper usage.
 If all that you want to use a kobject for is to provide a reference counter
 for your structure, please use the struct kref instead; a kobject would be
 overkill.  For more information on how to use struct kref, please see the
-file Documentation/kref.txt in the Linux kernel source tree.
+file Documentation/core-api/kref.rst in the Linux kernel source tree.
 
 
 Creating "simple" kobjects
@@ -222,17 +222,17 @@ ksets, show and store functions, and other details.  This is the one
 exception where a single kobject should be created.  To create such an
 entry, use the function::
 
-    struct kobject *kobject_create_and_add(char *name, struct kobject *parent);
+    struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
 
 This function will create a kobject and place it in sysfs in the location
 underneath the specified parent kobject.  To create simple attributes
 associated with this kobject, use::
 
-    int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
+    int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
 
 or::
 
-    int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp);
+    int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
 
 Both types of attributes used here, with a kobject that has been created
 with the kobject_create_and_add(), can be of type kobj_attribute, so no
@@ -300,8 +300,10 @@ kobj_type::
             void (*release)(struct kobject *kobj);
             const struct sysfs_ops *sysfs_ops;
             struct attribute **default_attrs;
+            const struct attribute_group **default_groups;
             const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
             const void *(*namespace)(struct kobject *kobj);
+            void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
     };
 
 This structure is used to describe a particular type of kobject (or, more
@@ -352,12 +354,12 @@ created and never declared statically or on the stack.  To create a new
 kset use::
 
   struct kset *kset_create_and_add(const char *name,
-                                   struct kset_uevent_ops *u,
-                                   struct kobject *parent);
+                                   const struct kset_uevent_ops *uevent_ops,
+                                   struct kobject *parent_kobj);
 
 When you are finished with the kset, call::
 
-  void kset_unregister(struct kset *kset);
+  void kset_unregister(struct kset *k);
 
 to destroy it.  This removes the kset from sysfs and decrements its reference
 count.  When the reference count goes to zero, the kset will be released.
@@ -371,9 +373,9 @@ If a kset wishes to control the uevent operations of the kobjects
 associated with it, it can use the struct kset_uevent_ops to handle it::
 
   struct kset_uevent_ops {
-          int (*filter)(struct kset *kset, struct kobject *kobj);
-          const char *(*name)(struct kset *kset, struct kobject *kobj);
-          int (*uevent)(struct kset *kset, struct kobject *kobj,
+          int (* const filter)(struct kset *kset, struct kobject *kobj);
+          const char *(* const name)(struct kset *kset, struct kobject *kobj);
+          int (* const uevent)(struct kset *kset, struct kobject *kobj,
                         struct kobj_uevent_env *env);
   };
 
diff --git a/Documentation/core-api/printk-basics.rst b/Documentation/core-api/printk-basics.rst
new file mode 100644 (file)
index 0000000..563a9ce
--- /dev/null
@@ -0,0 +1,115 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+Message logging with printk
+===========================
+
+printk() is one of the most widely known functions in the Linux kernel. It's the
+standard tool we have for printing messages and usually the most basic way of
+tracing and debugging. If you're familiar with printf(3) you can tell printk()
+is based on it, although it has some functional differences:
+
+  - printk() messages can specify a log level.
+
+  - the format string, while largely compatible with C99, doesn't follow the
+    exact same specification. It has some extensions and a few limitations
+    (no ``%n`` or floating point conversion specifiers). See :ref:`How to get
+    printk format specifiers right <printk-specifiers>`.
+
+All printk() messages are printed to the kernel log buffer, which is a ring
+buffer exported to userspace through /dev/kmsg. The usual way to read it is
+using ``dmesg``.
+
+printk() is typically used like this::
+
+  printk(KERN_INFO "Message: %s\n", arg);
+
+where ``KERN_INFO`` is the log level (note that it's concatenated to the format
+string, the log level is not a separate argument). The available log levels are:
+
++----------------+--------+-----------------------------------------------+
+| Name           | String |  Alias function                               |
++================+========+===============================================+
+| KERN_EMERG     | "0"    | pr_emerg()                                    |
++----------------+--------+-----------------------------------------------+
+| KERN_ALERT     | "1"    | pr_alert()                                    |
++----------------+--------+-----------------------------------------------+
+| KERN_CRIT      | "2"    | pr_crit()                                     |
++----------------+--------+-----------------------------------------------+
+| KERN_ERR       | "3"    | pr_err()                                      |
++----------------+--------+-----------------------------------------------+
+| KERN_WARNING   | "4"    | pr_warn()                                     |
++----------------+--------+-----------------------------------------------+
+| KERN_NOTICE    | "5"    | pr_notice()                                   |
++----------------+--------+-----------------------------------------------+
+| KERN_INFO      | "6"    | pr_info()                                     |
++----------------+--------+-----------------------------------------------+
+| KERN_DEBUG     | "7"    | pr_debug() and pr_devel() if DEBUG is defined |
++----------------+--------+-----------------------------------------------+
+| KERN_DEFAULT   | ""     |                                               |
++----------------+--------+-----------------------------------------------+
+| KERN_CONT      | "c"    | pr_cont()                                     |
++----------------+--------+-----------------------------------------------+
+
+
+The log level specifies the importance of a message. The kernel decides whether
+to show the message immediately (printing it to the current console) depending
+on its log level and the current *console_loglevel* (a kernel variable). If the
+message priority is higher (lower log level value) than the *console_loglevel*
+the message will be printed to the console.
+
+If the log level is omitted, the message is printed with ``KERN_DEFAULT``
+level.
+
+You can check the current *console_loglevel* with::
+
+  $ cat /proc/sys/kernel/printk
+  4        4        1        7
+
+The result shows the *current*, *default*, *minimum* and *boot-time-default* log
+levels.
+
+To change the current console_loglevel simply write the the desired level to
+``/proc/sys/kernel/printk``. For example, to print all messages to the console::
+
+  # echo 8 > /proc/sys/kernel/printk
+
+Another way, using ``dmesg``::
+
+  # dmesg -n 5
+
+sets the console_loglevel to print KERN_WARNING (4) or more severe messages to
+console. See ``dmesg(1)`` for more information.
+
+As an alternative to printk() you can use the ``pr_*()`` aliases for
+logging. This family of macros embed the log level in the macro names. For
+example::
+
+  pr_info("Info message no. %d\n", msg_num);
+
+prints a ``KERN_INFO`` message.
+
+Besides being more concise than the equivalent printk() calls, they can use a
+common definition for the format string through the pr_fmt() macro. For
+instance, defining this at the top of a source file (before any ``#include``
+directive)::
+
+  #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
+
+would prefix every pr_*() message in that file with the module and function name
+that originated the message.
+
+For debugging purposes there are also two conditionally-compiled macros:
+pr_debug() and pr_devel(), which are compiled-out unless ``DEBUG`` (or
+also ``CONFIG_DYNAMIC_DEBUG`` in the case of pr_debug()) is defined.
+
+
+Function reference
+==================
+
+.. kernel-doc:: kernel/printk/printk.c
+   :functions: printk
+
+.. kernel-doc:: include/linux/printk.h
+   :functions: pr_emerg pr_alert pr_crit pr_err pr_warn pr_notice pr_info
+      pr_fmt pr_debug pr_devel pr_cont
index 5dfcc4592b23efe335673ea13ba34fbbe4da6338..8c9aba262b1ea3c082467232169c65167c49b0f9 100644 (file)
@@ -2,6 +2,8 @@
 How to get printk format specifiers right
 =========================================
 
+.. _printk-specifiers:
+
 :Author: Randy Dunlap <rdunlap@infradead.org>
 :Author: Andrew Murray <amurray@mpc-data.co.uk>
 
@@ -482,21 +484,23 @@ Examples (OF)::
        %pfwf   /ocp@68000000/i2c@48072000/camera@10/port/endpoint - Full name
        %pfwP   endpoint                                - Node name
 
-Time and date (struct rtc_time)
--------------------------------
+Time and date
+-------------
 
 ::
 
-       %ptR            YYYY-mm-ddTHH:MM:SS
-       %ptRd           YYYY-mm-dd
-       %ptRt           HH:MM:SS
-       %ptR[dt][r]
+       %pt[RT]                 YYYY-mm-ddTHH:MM:SS
+       %pt[RT]d                YYYY-mm-dd
+       %pt[RT]t                HH:MM:SS
+       %pt[RT][dt][r]
 
-For printing date and time as represented by struct rtc_time structure in
-human readable format.
+For printing date and time as represented by
+       R  struct rtc_time structure
+       T  time64_t type
+in human readable format.
 
-By default year will be incremented by 1900 and month by 1. Use %ptRr (raw)
-to suppress this behaviour.
+By default year will be incremented by 1900 and month by 1.
+Use %pt[RT]r (raw) to suppress this behaviour.
 
 Passed by reference.
 
index 49d9833af8719dba25db48badf01ca961f056b4d..ec575e72d0b2ab7da24924aacd3ed61324533c53 100644 (file)
@@ -5,8 +5,9 @@ Memory Protection Keys
 ======================
 
 Memory Protection Keys for Userspace (PKU aka PKEYs) is a feature
-which is found on Intel's Skylake "Scalable Processor" Server CPUs.
-It will be avalable in future non-server parts.
+which is found on Intel's Skylake (and later) "Scalable Processor"
+Server CPUs. It will be available in future non-server Intel parts
+and future AMD processors.
 
 For anyone wishing to test or use this feature, it is available in
 Amazon's EC2 C5 instances and is known to work there using an Ubuntu
diff --git a/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml b/Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
new file mode 100644 (file)
index 0000000..84ae4cd
--- /dev/null
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/baikal,bt1-pvt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Baikal-T1 PVT Sensor
+
+maintainers:
+  - Serge Semin <fancer.lancer@gmail.com>
+
+description: |
+  Baikal-T1 SoC provides an embedded process, voltage and temperature
+  sensor to monitor an internal SoC environment (chip temperature, supply
+  voltage and process monitor) and on time detect critical situations,
+  which may cause the system instability and even damages. The IP-block
+  is based on the Analog Bits PVT sensor, but is equipped with a dedicated
+  control wrapper, which provides a MMIO registers-based access to the
+  sensor core functionality (APB3-bus based) and exposes an additional
+  functions like thresholds/data ready interrupts, its status and masks,
+  measurements timeout. Its internal structure is depicted on the next
+  diagram:
+
+     Analog Bits core                     Bakal-T1 PVT control block
+  +--------------------+                  +------------------------+
+  | Temperature sensor |-+         +------| Sensors control        |
+  |--------------------| |<---En---|      |------------------------|
+  | Voltage sensor     |-|<--Mode--| +--->| Sampled data           |
+  |--------------------| |<--Trim--+ |    |------------------------|
+  | Low-Vt sensor      |-|           | +--| Thresholds comparator  |
+  |--------------------| |---Data----| |  |------------------------|
+  | High-Vt sensor     |-|           | +->| Interrupts status      |
+  |--------------------| |--Valid--+-+ |  |------------------------|
+  | Standard-Vt sensor |-+         +---+--| Interrupts mask        |
+  +--------------------+                  |------------------------|
+           ^                              | Interrupts timeout     |
+           |                              +------------------------+
+           |                                        ^  ^
+  Rclk-----+----------------------------------------+  |
+  APB3-------------------------------------------------+
+
+  This bindings describes the external Baikal-T1 PVT control interfaces
+  like MMIO registers space, interrupt request number and clocks source.
+  These are then used by the corresponding hwmon device driver to
+  implement the sysfs files-based access to the sensors functionality.
+
+properties:
+  compatible:
+    const: baikal,bt1-pvt
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: PVT reference clock
+      - description: APB3 interface clock
+
+  clock-names:
+    items:
+      - const: ref
+      - const: pclk
+
+  "#thermal-sensor-cells":
+    description: Baikal-T1 can be referenced as the CPU thermal-sensor
+    const: 0
+
+  baikal,pvt-temp-offset-millicelsius:
+    description: |
+      Temperature sensor trimming factor. It can be used to manually adjust the
+      temperature measurements within 7.130 degrees Celsius.
+    maxItems: 1
+    items:
+      default: 0
+      minimum: 0
+      maximum: 7130
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/mips-gic.h>
+
+    pvt@1f200000 {
+      compatible = "baikal,bt1-pvt";
+      reg = <0x1f200000 0x1000>;
+      #thermal-sensor-cells = <0>;
+
+      interrupts = <GIC_SHARED 31 IRQ_TYPE_LEVEL_HIGH>;
+
+      baikal,pvt-temp-trim-millicelsius = <1000>;
+
+      clocks = <&ccu_sys>, <&ccu_sys>;
+      clock-names = "ref", "pclk";
+    };
+...
diff --git a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml
new file mode 100644 (file)
index 0000000..487a844
--- /dev/null
@@ -0,0 +1,196 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/gateworks-gsc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Gateworks System Controller
+
+description: |
+  The Gateworks System Controller (GSC) is a device present across various
+  Gateworks product families that provides a set of system related features
+  such as the following (refer to the board hardware user manuals to see what
+  features are present)
+   - Watchdog Timer
+   - GPIO
+   - Pushbutton controller
+   - Hardware monitor with ADC's for temperature and voltage rails and
+     fan controller
+
+maintainers:
+  - Tim Harvey <tharvey@gateworks.com>
+  - Robert Jones <rjones@gateworks.com>
+
+properties:
+  $nodename:
+    pattern: "gsc@[0-9a-f]{1,2}"
+  compatible:
+    const: gw,gsc
+
+  reg:
+    description: I2C device address
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  adc:
+    type: object
+    description: Optional hardware monitoring module
+
+    properties:
+      compatible:
+        const: gw,gsc-adc
+
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+    patternProperties:
+      "^channel@[0-9]+$":
+        type: object
+        description: |
+          Properties for a single ADC which can report cooked values
+          (i.e. temperature sensor based on thermister), raw values
+          (i.e. voltage rail with a pre-scaling resistor divider).
+
+        properties:
+          reg:
+            description: Register of the ADC
+            maxItems: 1
+
+          label:
+            description: Name of the ADC input
+
+          gw,mode:
+            description: |
+              conversion mode:
+                0 - temperature, in C*10
+                1 - pre-scaled voltage value
+                2 - scaled voltage based on an optional resistor divider
+                    and optional offset
+            $ref: /schemas/types.yaml#/definitions/uint32
+            enum: [0, 1, 2]
+
+          gw,voltage-divider-ohms:
+            description: Values of resistors for divider on raw ADC input
+            maxItems: 2
+            items:
+             minimum: 1000
+             maximum: 1000000
+
+          gw,voltage-offset-microvolt:
+            description: |
+              A positive voltage offset to apply to a raw ADC
+              (i.e. to compensate for a diode drop).
+            minimum: 0
+            maximum: 1000000
+
+        required:
+          - gw,mode
+          - reg
+          - label
+
+    required:
+      - compatible
+      - "#address-cells"
+      - "#size-cells"
+
+patternProperties:
+  "^fan-controller@[0-9a-f]+$":
+    type: object
+    description: Optional fan controller
+
+    properties:
+      compatible:
+        const: gw,gsc-fan
+
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      reg:
+        description: The fan controller base address
+        maxItems: 1
+
+    required:
+      - compatible
+      - reg
+      - "#address-cells"
+      - "#size-cells"
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - "#interrupt-cells"
+  - "#address-cells"
+  - "#size-cells"
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        gsc@20 {
+            compatible = "gw,gsc";
+            reg = <0x20>;
+            interrupt-parent = <&gpio1>;
+            interrupts = <4 GPIO_ACTIVE_LOW>;
+            interrupt-controller;
+            #interrupt-cells = <1>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            adc {
+                compatible = "gw,gsc-adc";
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                channel@0 { /* A0: Board Temperature */
+                    reg = <0x00>;
+                    label = "temp";
+                    gw,mode = <0>;
+                };
+
+                channel@2 { /* A1: Input Voltage (raw ADC) */
+                    reg = <0x02>;
+                    label = "vdd_vin";
+                    gw,mode = <1>;
+                    gw,voltage-divider-ohms = <22100 1000>;
+                    gw,voltage-offset-microvolt = <800000>;
+                };
+
+                channel@b { /* A2: Battery voltage */
+                    reg = <0x0b>;
+                    label = "vdd_bat";
+                    gw,mode = <1>;
+                };
+            };
+
+            fan-controller@2c {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                compatible = "gw,gsc-fan";
+                reg = <0x2c>;
+            };
+        };
+    };
index 5f2f07c09c9018c9fb65e80239e0e2a95bdbf418..4ed52184d081433774715f2f38efa2c759438a3b 100644 (file)
@@ -73,6 +73,8 @@ number as described in MAX8998 datasheet.
        - ESAFEOUT1: (ldo19)
        - ESAFEOUT2: (ld020)
 
+       - CHARGER: main battery charger current control
+
 Standard regulator bindings are used inside regulator subnodes. Check
   Documentation/devicetree/bindings/regulator/regulator.txt
 for more details.
@@ -113,5 +115,11 @@ Example:
                                regulator-always-on;
                                regulator-boot-on;
                        };
+
+                       charger_reg: CHARGER {
+                               regulator-name = "CHARGER";
+                               regulator-min-microamp = <90000>;
+                               regulator-max-microamp = <800000>;
+                       };
                };
        };
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-mx-sdhc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-mx-sdhc.yaml
new file mode 100644 (file)
index 0000000..7a386a5
--- /dev/null
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/amlogic,meson-mx-sdhc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Meson SDHC controller Device Tree Bindings
+
+allOf:
+  - $ref: "mmc-controller.yaml"
+
+maintainers:
+  - Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+
+description: |
+  The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
+  card interface with 1/4/8-bit bus width.
+  It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
+
+properties:
+  compatible:
+    items:
+      - enum:
+        - amlogic,meson8-sdhc
+        - amlogic,meson8b-sdhc
+        - amlogic,meson8m2-sdhc
+      - const: amlogic,meson-mx-sdhc
+
+  reg:
+    minItems: 1
+
+  interrupts:
+    minItems: 1
+
+  clocks:
+    minItems: 5
+
+  clock-names:
+    items:
+      - const: clkin0
+      - const: clkin1
+      - const: clkin2
+      - const: clkin3
+      - const: pclk
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    sdhc: mmc@8e00 {
+      compatible = "amlogic,meson8-sdhc", "amlogic,meson-mx-sdhc";
+      reg = <0x8e00 0x42>;
+      interrupts = <GIC_SPI 78 IRQ_TYPE_EDGE_RISING>;
+      clocks = <&xtal>,
+               <&fclk_div4>,
+               <&fclk_div3>,
+               <&fclk_div5>,
+               <&sdhc_pclk>;
+      clock-names = "clkin0", "clkin1", "clkin2", "clkin3", "pclk";
+    };
index 428685eb2ded34cee58adbda49c6e918cb27fd7c..f29bf7dd2ecec6f957ec81999a8c897d2dc69561 100644 (file)
@@ -18,12 +18,21 @@ Required Properties:
     - "xlnx,zynqmp-8.9a": ZynqMP SDHCI 8.9a PHY
       For this device it is strongly suggested to include clock-output-names and
       #clock-cells.
+    - "xlnx,versal-8.9a": Versal SDHCI 8.9a PHY
+      For this device it is strongly suggested to include clock-output-names and
+      #clock-cells.
     - "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
        Note: This binding has been deprecated and moved to [5].
     - "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel LGM eMMC PHY
       For this device it is strongly suggested to include arasan,soc-ctl-syscon.
     - "intel,lgm-sdhci-5.1-sdxc", "arasan,sdhci-5.1": Intel LGM SDXC PHY
       For this device it is strongly suggested to include arasan,soc-ctl-syscon.
+    - "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel Keem Bay eMMC
+      For this device it is strongly suggested to include arasan,soc-ctl-syscon.
+    - "intel,keembay-sdhci-5.1-sd": Intel Keem Bay SD controller
+      For this device it is strongly suggested to include arasan,soc-ctl-syscon.
+    - "intel,keembay-sdhci-5.1-sdio": Intel Keem Bay SDIO controller
+      For this device it is strongly suggested to include arasan,soc-ctl-syscon.
 
   [5] Documentation/devicetree/bindings/mmc/sdhci-am654.txt
 
@@ -104,6 +113,18 @@ Example:
                clk-phase-sd-hs = <63>, <72>;
        };
 
+       sdhci: mmc@f1040000 {
+               compatible = "xlnx,versal-8.9a", "arasan,sdhci-8.9a";
+               interrupt-parent = <&gic>;
+               interrupts = <0 126 4>;
+               reg = <0x0 0xf1040000 0x0 0x10000>;
+               clocks = <&clk200>, <&clk200>;
+               clock-names = "clk_xin", "clk_ahb";
+               clock-output-names = "clk_out_sd0", "clk_in_sd0";
+               #clock-cells = <1>;
+               clk-phase-sd-hs = <132>, <60>;
+       };
+
        emmc: sdhci@ec700000 {
                compatible = "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1";
                reg = <0xec700000 0x300>;
@@ -133,3 +154,39 @@ Example:
                phy-names = "phy_arasan";
                arasan,soc-ctl-syscon = <&sysconf>;
        };
+
+       mmc: mmc@33000000 {
+               compatible = "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1";
+               interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+               reg = <0x0 0x33000000 0x0 0x300>;
+               clock-names = "clk_xin", "clk_ahb";
+               clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>,
+                        <&scmi_clk KEEM_BAY_PSS_EMMC>;
+               phys = <&emmc_phy>;
+               phy-names = "phy_arasan";
+               assigned-clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>;
+               assigned-clock-rates = <200000000>;
+               clock-output-names = "emmc_cardclock";
+               #clock-cells = <0>;
+               arasan,soc-ctl-syscon = <&mmc_phy_syscon>;
+       };
+
+       sd0: mmc@31000000 {
+               compatible = "intel,keembay-sdhci-5.1-sd";
+               interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+               reg = <0x0 0x31000000 0x0 0x300>;
+               clock-names = "clk_xin", "clk_ahb";
+               clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD0>,
+                        <&scmi_clk KEEM_BAY_PSS_SD0>;
+               arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
+       };
+
+       sd1: mmc@32000000 {
+               compatible = "intel,keembay-sdhci-5.1-sdio";
+               interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+               reg = <0x0 0x32000000 0x0 0x300>;
+               clock-names = "clk_xin", "clk_ahb";
+               clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD1>,
+                        <&scmi_clk KEEM_BAY_PSS_SD1>;
+               arasan,soc-ctl-syscon = <&sd1_phy_syscon>;
+       };
index c064af5838aa4543cd7535450635dfb29a782fea..291532ac0446fc714e0184621beb014fea47ca2c 100644 (file)
@@ -11,6 +11,7 @@ Required properties:
        - "renesas,mmcif-r7s72100" for the MMCIF found in r7s72100 SoCs
        - "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
        - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
+       - "renesas,mmcif-r8a7742" for the MMCIF found in r8a7742 SoCs
        - "renesas,mmcif-r8a7743" for the MMCIF found in r8a7743 SoCs
        - "renesas,mmcif-r8a7744" for the MMCIF found in r8a7744 SoCs
        - "renesas,mmcif-r8a7745" for the MMCIF found in r8a7745 SoCs
@@ -24,8 +25,8 @@ Required properties:
 - interrupts: Some SoCs have only 1 shared interrupt, while others have either
   2 or 3 individual interrupts (error, int, card detect). Below is the number
   of interrupts for each SoC:
-    1: r8a73a4, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791, r8a7793,
-       r8a7794
+    1: r8a73a4, r8a7742, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791,
+       r8a7793, r8a7794
     2: r8a7740, sh73a0
     3: r7s72100
 
index e6cc47844207049b26f414ef0c12d954b45bea02..0ca9a622cce0b2fae6af6826376532b1b8ee717c 100644 (file)
@@ -7,6 +7,7 @@ Required properties:
                "renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC
                "renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
                "renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
+               "renesas,sdhi-r8a7742" - SDHI IP on R8A7742 SoC
                "renesas,sdhi-r8a7743" - SDHI IP on R8A7743 SoC
                "renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC
                "renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC
index 5445931c5ab9efe6a8a859ce7446667301446664..b8e1d2b7aea943e5c8738d55c39ccfeadb7eca13 100644 (file)
@@ -17,6 +17,7 @@ Required properties:
                "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
                "qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
                "qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
+               "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
                "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
                "qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
                "qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
@@ -46,6 +47,13 @@ Required properties:
        "cal"   - reference clock for RCLK delay calibration (optional)
        "sleep" - sleep clock for RCLK delay calibration (optional)
 
+- qcom,ddr-config: Certain chipsets and platforms require particular settings
+       for the DDR_CONFIG register. Use this field to specify the register
+       value as per the Hardware Programming Guide.
+
+- qcom,dll-config: Chipset and Platform specific value. Use this field to
+       specify the DLL_CONFIG register value as per Hardware Programming Guide.
+
 Example:
 
        sdhc_1: sdhci@f9824900 {
@@ -63,6 +71,9 @@ Example:
 
                clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
                clock-names = "core", "iface";
+
+               qcom,dll-config = <0x000f642c>;
+               qcom,ddr-config = <0x80040868>;
        };
 
        sdhc_2: sdhci@f98a4900 {
@@ -80,4 +91,7 @@ Example:
 
                clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
                clock-names = "core", "iface";
+
+               qcom,dll-config = <0x0007642c>;
+               qcom,ddr-config = <0x80040868>;
        };
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt
deleted file mode 100644 (file)
index 3d1b449..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-* Marvell sdhci-pxa v2/v3 controller
-
-This file documents differences between the core properties in mmc.txt
-and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers.
-
-Required properties:
-- compatible: Should be "mrvl,pxav2-mmc", "mrvl,pxav3-mmc" or
-  "marvell,armada-380-sdhci".
-- reg:
-  * for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
-    the SDHCI registers.
-
-  * for "marvell,armada-380-sdhci", three register areas. The first
-    one for the SDHCI registers themselves, the second one for the
-    AXI/Mbus bridge registers of the SDHCI unit, the third one for the
-    SDIO3 Configuration register
-- reg names: should be "sdhci", "mbus", "conf-sdio3". only mandatory
-  for "marvell,armada-380-sdhci"
-- clocks: Array of clocks required for SDHCI; requires at least one for
-    I/O clock.
-- clock-names: Array of names corresponding to clocks property; shall be
-    "io" for I/O clock and "core" for optional core clock.
-
-Optional properties:
-- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
-
-Example:
-
-sdhci@d4280800 {
-       compatible = "mrvl,pxav3-mmc";
-       reg = <0xd4280800 0x800>;
-       bus-width = <8>;
-       interrupts = <27>;
-       clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
-       clock-names = "io", "core";
-       non-removable;
-       mrvl,clk-delay-cycles = <31>;
-};
-
-sdhci@d8000 {
-       compatible = "marvell,armada-380-sdhci";
-       reg-names = "sdhci", "mbus", "conf-sdio3";
-       reg = <0xd8000 0x1000>,
-               <0xdc000 0x100>;
-               <0x18454 0x4>;
-       interrupts = <0 25 0x4>;
-       clocks = <&gateclk 17>;
-       clock-names = "io";
-       mrvl,clk-delay-cycles = <0x1F>;
-};
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml b/Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml
new file mode 100644 (file)
index 0000000..a58715c
--- /dev/null
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/sdhci-pxa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell PXA SDHCI v2/v3 bindings
+
+maintainers:
+  - Ulf Hansson <ulf.hansson@linaro.org>
+
+allOf:
+  - $ref: mmc-controller.yaml#
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: marvell,armada-380-sdhci
+    then:
+      properties:
+        regs:
+          minItems: 3
+        reg-names:
+          minItems: 3
+      required:
+        - reg-names
+    else:
+      properties:
+        regs:
+          maxItems: 1
+        reg-names:
+          maxItems: 1
+
+properties:
+  compatible:
+    enum:
+      - mrvl,pxav2-mmc
+      - mrvl,pxav3-mmc
+      - marvell,armada-380-sdhci
+
+  reg:
+    minItems: 1
+    maxItems: 3
+
+  reg-names:
+    items:
+      - const: sdhci
+      - const: mbus
+      - const: conf-sdio3
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: io
+      - const: core
+
+  mrvl,clk-delay-cycles:
+    description: Specify a number of cycles to delay for tuning.
+    $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+examples:
+  - |
+    #include <dt-bindings/clock/berlin2.h>
+    mmc@d4280800 {
+        compatible = "mrvl,pxav3-mmc";
+        reg = <0xd4280800 0x800>;
+        bus-width = <8>;
+        interrupts = <27>;
+        clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
+        clock-names = "io", "core";
+        non-removable;
+        mrvl,clk-delay-cycles = <31>;
+    };
+  - |
+    mmc@d8000 {
+        compatible = "marvell,armada-380-sdhci";
+        reg-names = "sdhci", "mbus", "conf-sdio3";
+        reg = <0xd8000 0x1000>,
+              <0xdc000 0x100>,
+              <0x18454 0x4>;
+        interrupts = <0 25 0x4>;
+        clocks = <&gateclk 17>;
+        clock-names = "io";
+        mrvl,clk-delay-cycles = <0x1F>;
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt
deleted file mode 100644 (file)
index a3106c7..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-Anatop Voltage regulators
-
-Required properties:
-- compatible: Must be "fsl,anatop-regulator"
-- regulator-name: A string used as a descriptive name for regulator outputs
-- anatop-reg-offset: Anatop MFD register offset
-- anatop-vol-bit-shift: Bit shift for the register
-- anatop-vol-bit-width: Number of bits used in the register
-- anatop-min-bit-val: Minimum value of this register
-- anatop-min-voltage: Minimum voltage of this regulator
-- anatop-max-voltage: Maximum voltage of this regulator
-
-Optional properties:
-- anatop-delay-reg-offset: Anatop MFD step time register offset
-- anatop-delay-bit-shift: Bit shift for the step time register
-- anatop-delay-bit-width: Number of bits used in the step time register
-- vin-supply: The supply for this regulator
-- anatop-enable-bit: Regulator enable bit offset
-
-Any property defined as part of the core regulator
-binding, defined in regulator.txt, can also be used.
-
-Example:
-
-       regulator-vddpu {
-               compatible = "fsl,anatop-regulator";
-               regulator-name = "vddpu";
-               regulator-min-microvolt = <725000>;
-               regulator-max-microvolt = <1300000>;
-               regulator-always-on;
-               anatop-reg-offset = <0x140>;
-               anatop-vol-bit-shift = <9>;
-               anatop-vol-bit-width = <5>;
-               anatop-delay-reg-offset = <0x170>;
-               anatop-delay-bit-shift = <24>;
-               anatop-delay-bit-width = <2>;
-               anatop-min-bit-val = <1>;
-               anatop-min-voltage = <725000>;
-               anatop-max-voltage = <1300000>;
-       };
diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.yaml b/Documentation/devicetree/bindings/regulator/anatop-regulator.yaml
new file mode 100644 (file)
index 0000000..e7b3abe
--- /dev/null
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/anatop-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Anatop Voltage Regulators
+
+maintainers:
+  - Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+
+allOf:
+  - $ref: "regulator.yaml#"
+
+properties:
+  compatible:
+    const: fsl,anatop-regulator
+
+  regulator-name: true
+
+  anatop-reg-offset:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the anatop MFD register offset.
+
+  anatop-vol-bit-shift:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the bit shift for the register.
+
+  anatop-vol-bit-width:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the number of bits used in the register.
+
+  anatop-min-bit-val:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the minimum value of this register.
+
+  anatop-min-voltage:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the minimum voltage of this regulator.
+
+  anatop-max-voltage:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the maximum voltage of this regulator.
+
+  anatop-delay-reg-offset:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the anatop MFD step time register offset.
+
+  anatop-delay-bit-shift:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the bit shift for the step time register.
+
+  anatop-delay-bit-width:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing the number of bits used in the step time register.
+
+  anatop-enable-bit:
+    $ref: '/schemas/types.yaml#/definitions/uint32'
+    description: u32 value representing regulator enable bit offset.
+
+  vin-supply:
+    $ref: '/schemas/types.yaml#/definitions/phandle'
+    description: input supply phandle.
+
+required:
+  - compatible
+  - regulator-name
+  - anatop-reg-offset
+  - anatop-vol-bit-shift
+  - anatop-vol-bit-width
+  - anatop-min-bit-val
+  - anatop-min-voltage
+  - anatop-max-voltage
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    regulator-vddpu {
+        compatible = "fsl,anatop-regulator";
+        regulator-name = "vddpu";
+        regulator-min-microvolt = <725000>;
+        regulator-max-microvolt = <1300000>;
+        regulator-always-on;
+        anatop-reg-offset = <0x140>;
+        anatop-vol-bit-shift = <9>;
+        anatop-vol-bit-width = <5>;
+        anatop-delay-reg-offset = <0x170>;
+        anatop-delay-bit-shift = <24>;
+        anatop-delay-bit-width = <2>;
+        anatop-min-bit-val = <1>;
+        anatop-min-voltage = <725000>;
+        anatop-max-voltage = <1300000>;
+    };
diff --git a/Documentation/devicetree/bindings/regulator/maxim,max77826.yaml b/Documentation/devicetree/bindings/regulator/maxim,max77826.yaml
new file mode 100644 (file)
index 0000000..19cbd5e
--- /dev/null
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/maxim,max77826.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX77826 PMIC
+
+maintainers:
+  - Iskren Chernev <iskren.chernev@gmail.com>
+
+properties:
+  $nodename:
+    pattern: "pmic@[0-9a-f]{1,2}"
+  compatible:
+    enum:
+      - maxim,max77826
+
+  reg:
+    maxItems: 1
+
+  regulators:
+    type: object
+    allOf:
+      - $ref: regulator.yaml#
+    description: |
+      list of regulators provided by this controller, must be named
+      after their hardware counterparts LDO[1-15], BUCK and BUCKBOOST
+
+    patternProperties:
+      "^LDO([1-9]|1[0-5])$":
+        type: object
+        allOf:
+          - $ref: regulator.yaml#
+
+      "^BUCK|BUCKBOOST$":
+        type: object
+        allOf:
+          - $ref: regulator.yaml#
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - regulators
+
+additionalProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        pmic@69 {
+            compatible = "maxim,max77826";
+            reg = <0x69>;
+
+            regulators {
+                LDO2 {
+                    regulator-min-microvolt = <650000>;
+                    regulator-max-microvolt = <3587500>;
+                };
+            };
+       };
+     };
+...
index 0eba562fe5c6421e3cd29d9c5b022d7b3d31647a..b7886fea368ce107c0b007f4792cfc400ee248a1 100644 (file)
@@ -30,7 +30,7 @@ Optional properties:
 - ecc-size: enables ECC support and specifies ECC buffer size in bytes
   (defaults to 0: no ECC)
 
-- record-size: maximum size in bytes of each dump done on oops/panic
+- record-size: maximum size in bytes of each kmsg dump.
   (defaults to 0: disabled)
 
 - console-size: size in bytes of log buffer reserved for kernel messages
@@ -45,7 +45,16 @@ Optional properties:
 - unbuffered: if present, use unbuffered mappings to map the reserved region
   (defaults to buffered mappings)
 
-- no-dump-oops: if present, only dump panics (defaults to panics and oops)
+- max-reason: if present, sets maximum type of kmsg dump reasons to store
+  (defaults to 2: log Oopses and Panics). This can be set to INT_MAX to
+  store all kmsg dumps. See include/linux/kmsg_dump.h KMSG_DUMP_* for other
+  kmsg dump reason values. Setting this to 0 (KMSG_DUMP_UNDEF), means the
+  reason filtering will be controlled by the printk.always_kmsg_dump boot
+  param: if unset, it will be KMSG_DUMP_OOPS, otherwise KMSG_DUMP_MAX.
+
+- no-dump-oops: deprecated, use max_reason instead. If present, and
+  max_reason is not specified, it is equivalent to max_reason = 1
+  (KMSG_DUMP_PANIC).
 
 - flags: if present, pass ramoops behavioral flags (defaults to 0,
   see include/linux/pstore_ram.h RAMOOPS_FLAG_* for flag values).
diff --git a/Documentation/devicetree/bindings/rng/arm-cctrng.yaml b/Documentation/devicetree/bindings/rng/arm-cctrng.yaml
new file mode 100644 (file)
index 0000000..ca6aad1
--- /dev/null
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rng/arm-cctrng.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Arm TrustZone CryptoCell TRNG engine
+
+maintainers:
+  - Hadar Gat <hadar.gat@arm.com>
+
+description: |+
+  Arm TrustZone CryptoCell TRNG (True Random Number Generator) engine.
+
+properties:
+  compatible:
+    enum:
+      - arm,cryptocell-713-trng
+      - arm,cryptocell-703-trng
+
+  interrupts:
+    maxItems: 1
+
+  reg:
+    maxItems: 1
+
+  arm,rosc-ratio:
+    description:
+      Arm TrustZone CryptoCell TRNG engine has 4 ring oscillators.
+      Sampling ratio values for these 4 ring oscillators. (from calibration)
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32-array
+      - items:
+          maxItems: 4
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+  - interrupts
+  - reg
+  - arm,rosc-ratio
+
+additionalProperties: false
+
+examples:
+  - |
+    arm_cctrng: rng@60000000 {
+        compatible = "arm,cryptocell-713-trng";
+        interrupts = <0 29 4>;
+        reg = <0x60000000 0x10000>;
+        arm,rosc-ratio = <5000 1000 500 0>;
+    };
index ad7ac80a3841fb06d5b1962d378090a2e24a678a..f5e518d099f2c2057afe360b824c1d88bb2203b5 100644 (file)
@@ -26,6 +26,16 @@ Required properties:
     "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-qspi" : MSPI+BSPI on BRCMSTB SoCs
     "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
                                                   BRCMSTB  SoCs
+    "brcm,spi-bcm7425-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                                           BRCMSTB  SoCs
+    "brcm,spi-bcm7429-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                                           BRCMSTB  SoCs
+    "brcm,spi-bcm7435-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                                           BRCMSTB  SoCs
+    "brcm,spi-bcm7216-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                                           BRCMSTB  SoCs
+    "brcm,spi-bcm7278-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
+                                                                           BRCMSTB  SoCs
     "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi"     : MSPI+BSPI on Cygnus, NSP
     "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi"     : NS2 SoCs
 
diff --git a/Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml b/Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml
new file mode 100644 (file)
index 0000000..4ddb42a
--- /dev/null
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/mikrotik,rb4xx-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MikroTik RB4xx series SPI master
+
+maintainers:
+  - Gabor Juhos <juhosg@openwrt.org>
+  - Bert Vermeulen <bert@biot.com>
+
+allOf:
+  - $ref: "spi-controller.yaml#"
+
+properties:
+  compatible:
+    const: mikrotik,rb4xx-spi
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    spi: spi@1f000000 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        compatible = "mikrotik,rb4xx-spi";
+        reg = <0x1f000000 0x10>;
+    };
+
+...
\ No newline at end of file
diff --git a/Documentation/devicetree/bindings/spi/renesas,rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rspi.yaml
new file mode 100644 (file)
index 0000000..c54ac05
--- /dev/null
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/renesas,rspi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas (Quad) Serial Peripheral Interface (RSPI/QSPI)
+
+maintainers:
+  - Geert Uytterhoeven <geert+renesas@glider.be>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - renesas,rspi-sh7757    # SH7757
+          - const: renesas,rspi        # Legacy SH
+
+      - items:
+          - enum:
+              - renesas,rspi-r7s72100  # RZ/A1H
+              - renesas,rspi-r7s9210   # RZ/A2
+          - const: renesas,rspi-rz     # RZ/A
+
+      - items:
+          - enum:
+              - renesas,qspi-r8a7743   # RZ/G1M
+              - renesas,qspi-r8a7744   # RZ/G1N
+              - renesas,qspi-r8a7745   # RZ/G1E
+              - renesas,qspi-r8a77470  # RZ/G1C
+              - renesas,qspi-r8a7790   # R-Car H2
+              - renesas,qspi-r8a7791   # R-Car M2-W
+              - renesas,qspi-r8a7792   # R-Car V2H
+              - renesas,qspi-r8a7793   # R-Car M2-N
+              - renesas,qspi-r8a7794   # R-Car E2
+          - const: renesas,qspi        # R-Car Gen2 and RZ/G1
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    oneOf:
+      - items:
+          - description: A combined interrupt
+      - items:
+          - description: Error interrupt (SPEI)
+          - description: Receive Interrupt (SPRI)
+          - description: Transmit Interrupt (SPTI)
+
+  interrupt-names:
+    oneOf:
+      - items:
+          - const: mux
+      - items:
+          - const: error
+          - const: rx
+          - const: tx
+
+  clocks:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  dmas:
+    description:
+      Must contain a list of pairs of references to DMA specifiers, one for
+      transmission, and one for reception.
+
+  dma-names:
+    minItems: 2
+    maxItems: 4
+    items:
+      enum:
+        - tx
+        - rx
+
+  num-cs:
+    description: |
+      Total number of native chip selects.
+      Hardware limitations related to chip selects:
+        - When using GPIO chip selects, at least one native chip select must
+          be left unused, as it will be driven anyway.
+    minimum: 1
+    maximum: 2
+    default: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - power-domains
+  - '#address-cells'
+  - '#size-cells'
+
+allOf:
+  - $ref: spi-controller.yaml#
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - renesas,rspi-rz
+    then:
+      properties:
+        interrupts:
+          minItems: 3
+      required:
+        - interrupt-names
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - renesas,qspi
+    then:
+      required:
+        - resets
+
+examples:
+  - |
+    #include <dt-bindings/clock/r8a7791-cpg-mssr.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/r8a7791-sysc.h>
+
+    qspi: spi@e6b10000 {
+            compatible = "renesas,qspi-r8a7791", "renesas,qspi";
+            reg = <0xe6b10000 0x2c>;
+            interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&cpg CPG_MOD 917>;
+            dmas = <&dmac0 0x17>, <&dmac0 0x18>, <&dmac1 0x17>, <&dmac1 0x18>;
+            dma-names = "tx", "rx", "tx", "rx";
+            power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
+            resets = <&cpg 917>;
+            num-cs = <1>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt
deleted file mode 100644 (file)
index 3ed08ee..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
-
-Required properties:
-- compatible : "snps,dw-apb-ssi" or "mscc,<soc>-spi", where soc is "ocelot" or
-  "jaguar2", or "amazon,alpine-dw-apb-ssi"
-- reg : The register base for the controller. For "mscc,<soc>-spi", a second
-  register set is required (named ICPU_CFG:SPI_MST)
-- interrupts : One interrupt, used by the controller.
-- #address-cells : <1>, as required by generic SPI binding.
-- #size-cells : <0>, also as required by generic SPI binding.
-- clocks : phandles for the clocks, see the description of clock-names below.
-   The phandle for the "ssi_clk" is required. The phandle for the "pclk" clock
-   is optional. If a single clock is specified but no clock-name, it is the
-   "ssi_clk" clock. If both clocks are listed, the "ssi_clk" must be first.
-
-Optional properties:
-- clock-names : Contains the names of the clocks:
-    "ssi_clk", for the core clock used to generate the external SPI clock.
-    "pclk", the interface clock, required for register access. If a clock domain
-     used to enable this clock then it should be named "pclk_clkdomain".
-- cs-gpios : Specifies the gpio pins to be used for chipselects.
-- num-cs : The number of chipselects. If omitted, this will default to 4.
-- reg-io-width : The I/O register width (in bytes) implemented by this
-  device.  Supported values are 2 or 4 (the default).
-
-Child nodes as per the generic SPI binding.
-
-Example:
-
-       spi@fff00000 {
-               compatible = "snps,dw-apb-ssi";
-               reg = <0xfff00000 0x1000>;
-               interrupts = <0 154 4>;
-               #address-cells = <1>;
-               #size-cells = <0>;
-               clocks = <&spi_m_clk>;
-               num-cs = <2>;
-               cs-gpios = <&gpio0 13 0>,
-                          <&gpio0 14 0>;
-       };
-
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
new file mode 100644 (file)
index 0000000..c62cbe7
--- /dev/null
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/snps,dw-apb-ssi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface
+
+maintainers:
+  - Mark Brown <broonie@kernel.org>
+
+allOf:
+  - $ref: "spi-controller.yaml#"
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - mscc,ocelot-spi
+              - mscc,jaguar2-spi
+    then:
+      properties:
+        reg:
+          minItems: 2
+
+properties:
+  compatible:
+    oneOf:
+      - description: Generic DW SPI Controller
+        enum:
+          - snps,dw-apb-ssi
+          - snps,dwc-ssi-1.01a
+      - description: Microsemi Ocelot/Jaguar2 SoC SPI Controller
+        items:
+          - enum:
+              - mscc,ocelot-spi
+              - mscc,jaguar2-spi
+          - const: snps,dw-apb-ssi
+      - description: Amazon Alpine SPI Controller
+        const: amazon,alpine-dw-apb-ssi
+      - description: Renesas RZ/N1 SPI Controller
+        items:
+          - const: renesas,rzn1-spi
+          - const: snps,dw-apb-ssi
+      - description: Intel Keem Bay SPI Controller
+        const: intel,keembay-ssi
+
+  reg:
+    minItems: 1
+    items:
+      - description: DW APB SSI controller memory mapped registers
+      - description: SPI MST region map
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    items:
+      - description: SPI Controller reference clock source
+      - description: APB interface clock source
+
+  clock-names:
+    minItems: 1
+    items:
+      - const: ssi_clk
+      - const: pclk
+
+  resets:
+    maxItems: 1
+
+  reset-names:
+    const: spi
+
+  reg-io-width:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: I/O register width (in bytes) implemented by this device
+    default: 4
+    enum: [ 2, 4 ]
+
+  num-cs:
+    default: 4
+    minimum: 1
+    maximum: 4
+
+  dmas:
+    items:
+      - description: TX DMA Channel
+      - description: RX DMA Channel
+
+  dma-names:
+    items:
+      - const: tx
+      - const: rx
+
+patternProperties:
+  "^.*@[0-9a-f]+$":
+    type: object
+    properties:
+      reg:
+        minimum: 0
+        maximum: 3
+
+      spi-rx-bus-width:
+        const: 1
+
+      spi-tx-bus-width:
+        const: 1
+
+unevaluatedProperties: false
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - interrupts
+  - clocks
+
+examples:
+  - |
+    spi@fff00000 {
+      compatible = "snps,dw-apb-ssi";
+      reg = <0xfff00000 0x1000>;
+      #address-cells = <1>;
+      #size-cells = <0>;
+      interrupts = <0 154 4>;
+      clocks = <&spi_m_clk>;
+      num-cs = <2>;
+      cs-gpios = <&gpio0 13 0>,
+                 <&gpio0 14 0>;
+    };
+...
diff --git a/Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml b/Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml
new file mode 100644 (file)
index 0000000..c254092
--- /dev/null
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/socionext,uniphier-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Socionext UniPhier SPI controller
+
+description: |
+  UniPhier SoCs have SCSSI which supports SPI single channel.
+
+maintainers:
+  - Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+  - Keiji Hayashibara <hayashibara.keiji@socionext.com>
+
+allOf:
+  - $ref: spi-controller.yaml#
+
+properties:
+  "#address-cells": true
+  "#size-cells": true
+
+  compatible:
+    const: socionext,uniphier-scssi
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - resets
+  - "#address-cells"
+  - "#size-cells"
+
+examples:
+  - |
+    spi0: spi@54006000 {
+        compatible = "socionext,uniphier-scssi";
+        reg = <0x54006000 0x100>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        interrupts = <0 39 4>;
+        clocks = <&peri_clk 11>;
+        resets = <&peri_rst 11>;
+    };
diff --git a/Documentation/devicetree/bindings/spi/spi-dw.txt b/Documentation/devicetree/bindings/spi/spi-dw.txt
deleted file mode 100644 (file)
index 7b63ed6..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-Synopsys DesignWare SPI master
-
-Required properties:
-- compatible: should be "snps,designware-spi"
-- #address-cells: see spi-bus.txt
-- #size-cells: see spi-bus.txt
-- reg: address and length of the spi master registers
-- interrupts: should contain one interrupt
-- clocks: spi clock phandle
-- num-cs: see spi-bus.txt
-
-Optional properties:
-- cs-gpios: see spi-bus.txt
-
-Example:
-
-spi: spi@4020a000 {
-       compatible = "snps,designware-spi";
-       interrupts = <11 1>;
-       reg = <0x4020a000 0x1000>;
-       clocks = <&pclk>;
-       num-cs = <2>;
-       cs-gpios = <&banka 0 0>;
-};
diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt
deleted file mode 100644 (file)
index 421722b..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-Device tree configuration for Renesas RSPI/QSPI driver
-
-Required properties:
-- compatible       : For Renesas Serial Peripheral Interface on legacy SH:
-                    "renesas,rspi-<soctype>", "renesas,rspi" as fallback.
-                    For Renesas Serial Peripheral Interface on RZ/A:
-                    "renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback.
-                    For Quad Serial Peripheral Interface on R-Car Gen2 and
-                    RZ/G1 devices:
-                    "renesas,qspi-<soctype>", "renesas,qspi" as fallback.
-                    Examples with soctypes are:
-                       - "renesas,rspi-sh7757" (SH)
-                       - "renesas,rspi-r7s72100" (RZ/A1H)
-                       - "renesas,rspi-r7s9210" (RZ/A2)
-                       - "renesas,qspi-r8a7743" (RZ/G1M)
-                       - "renesas,qspi-r8a7744" (RZ/G1N)
-                       - "renesas,qspi-r8a7745" (RZ/G1E)
-                       - "renesas,qspi-r8a77470" (RZ/G1C)
-                       - "renesas,qspi-r8a7790" (R-Car H2)
-                       - "renesas,qspi-r8a7791" (R-Car M2-W)
-                       - "renesas,qspi-r8a7792" (R-Car V2H)
-                       - "renesas,qspi-r8a7793" (R-Car M2-N)
-                       - "renesas,qspi-r8a7794" (R-Car E2)
-- reg              : Address start and address range size of the device
-- interrupts       : A list of interrupt-specifiers, one for each entry in
-                    interrupt-names.
-                    If interrupt-names is not present, an interrupt specifier
-                    for a single muxed interrupt.
-- interrupt-names  : A list of interrupt names. Should contain (if present):
-                      - "error" for SPEI,
-                      - "rx" for SPRI,
-                      - "tx" to SPTI,
-                      - "mux" for a single muxed interrupt.
-- num-cs          : Number of chip selects. Some RSPI cores have more than 1.
-- #address-cells   : Must be <1>
-- #size-cells      : Must be <0>
-
-Optional properties:
-- clocks           : Must contain a reference to the functional clock.
-- dmas             : Must contain a list of two references to DMA specifiers,
-                    one for transmission, and one for reception.
-- dma-names        : Must contain a list of two DMA names, "tx" and "rx".
-
-Pinctrl properties might be needed, too.  See
-Documentation/devicetree/bindings/pinctrl/renesas,*.
-
-Examples:
-
-       spi0: spi@e800c800 {
-               compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz";
-               reg = <0xe800c800 0x24>;
-               interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>,
-                            <0 239 IRQ_TYPE_LEVEL_HIGH>,
-                            <0 240 IRQ_TYPE_LEVEL_HIGH>;
-               interrupt-names = "error", "rx", "tx";
-               interrupt-parent = <&gic>;
-               num-cs = <1>;
-               #address-cells = <1>;
-               #size-cells = <0>;
-       };
-
-       spi: spi@e6b10000 {
-               compatible = "renesas,qspi-r8a7791", "renesas,qspi";
-               reg = <0 0xe6b10000 0 0x2c>;
-               interrupt-parent = <&gic>;
-               interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>;
-               clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>;
-               num-cs = <1>;
-               #address-cells = <1>;
-               #size-cells = <0>;
-               dmas = <&dmac0 0x17>, <&dmac0 0x18>;
-               dma-names = "tx", "rx";
-       };
diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt
deleted file mode 100644 (file)
index e120157..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-Socionext UniPhier SPI controller driver
-
-UniPhier SoCs have SCSSI which supports SPI single channel.
-
-Required properties:
- - compatible: should be "socionext,uniphier-scssi"
- - reg: address and length of the spi master registers
- - #address-cells: must be <1>, see spi-bus.txt
- - #size-cells: must be <0>, see spi-bus.txt
- - interrupts: a single interrupt specifier
- - pinctrl-names: should be "default"
- - pinctrl-0: pin control state for the default mode
- - clocks: a phandle to the clock for the device
- - resets: a phandle to the reset control for the device
-
-Example:
-
-spi0: spi@54006000 {
-       compatible = "socionext,uniphier-scssi";
-       reg = <0x54006000 0x100>;
-       #address-cells = <1>;
-       #size-cells = <0>;
-       interrupts = <0 39 4>;
-       pinctrl-names = "default";
-       pinctrl-0 = <&pinctrl_spi0>;
-       clocks = <&peri_clk 11>;
-       resets = <&peri_rst 11>;
-};
index e65fde4a73883f0a9457200a79a4d8e91ffeca44..47b184bce41439cb3a8d5f05071d0a5003843a8c 100644 (file)
@@ -29,7 +29,7 @@ modification to bootloader.
 Example:
 
 For am4372:
-qspi: qspi@4b300000 {
+qspi: qspi@47900000 {
        compatible = "ti,am4372-qspi";
        reg = <0x47900000 0x100>, <0x30000000 0x4000000>;
        reg-names = "qspi_base", "qspi_mmap";
index d3891386d6710effe562acad382bf71f7fa16411..d3277fe6640bd8b0c7bc50d2f3da493172edf07d 100644 (file)
@@ -633,6 +633,8 @@ patternProperties:
     description: Microsoft Corporation
   "^mikroe,.*":
     description: MikroElektronika d.o.o.
+  "^mikrotik,.*":
+    description: MikroTik
   "^miniand,.*":
     description: Miniand Tech
   "^minix,.*":
index 5afc0ddba40aa380932b3255ad3e8920af2dccb4..755d39f0d407537b92bf7b243fdec1a2569da441 100644 (file)
@@ -6,7 +6,7 @@ Documentation subsystem maintainer entry profile
 The documentation "subsystem" is the central coordinating point for the
 kernel's documentation and associated infrastructure.  It covers the
 hierarchy under Documentation/ (with the exception of
-Documentation/device-tree), various utilities under scripts/ and, at least
+Documentation/devicetree), various utilities under scripts/ and, at least
 some of the time, LICENSES/.
 
 It's worth noting, though, that the boundaries of this subsystem are rather
index c78db28519f740127a02ae956085e7a4ca9113b1..63dec76d1d8d0dfe7530f5965223f13e21107d56 100644 (file)
@@ -11,7 +11,7 @@ course not limited to GPU use cases.
 The three main components of this are: (1) dma-buf, representing a
 sg_table and exposed to userspace as a file descriptor to allow passing
 between devices, (2) fence, which provides a mechanism to signal when
-one device as finished access, and (3) reservation, which manages the
+one device has finished access, and (3) reservation, which manages the
 shared or exclusive fence(s) associated with the buffer.
 
 Shared DMA Buffers
@@ -31,7 +31,7 @@ The exporter
  - implements and manages operations in :c:type:`struct dma_buf_ops
    <dma_buf_ops>` for the buffer,
  - allows other users to share the buffer by using dma_buf sharing APIs,
- - manages the details of buffer allocation, wrapped int a :c:type:`struct
+ - manages the details of buffer allocation, wrapped in a :c:type:`struct
    dma_buf <dma_buf>`,
  - decides about the actual backing storage where this allocation happens,
  - and takes care of any migration of scatterlist - for all (shared) users of
index 2b868d49d3492d6ea3f563ccca5e1757eabea7c9..b9b022371e856e831a4c080cbea63e104b97cb01 100644 (file)
@@ -50,10 +50,10 @@ Attributes
 
 Attributes of devices can be exported by a device driver through sysfs.
 
-Please see Documentation/filesystems/sysfs.txt for more information
+Please see Documentation/filesystems/sysfs.rst for more information
 on how sysfs works.
 
-As explained in Documentation/kobject.txt, device attributes must be
+As explained in Documentation/core-api/kobject.rst, device attributes must be
 created before the KOBJ_ADD uevent is generated. The only way to realize
 that is by defining an attribute group.
 
index d4d1e9b40e0c7e46e63a9702ac369cab6c525d8e..e98d0ab4a9b6f80f11976b5a732d13568b3b0ddf 100644 (file)
@@ -121,4 +121,4 @@ device-specific data or tunable interfaces.
 
 More information about the sysfs directory layout can be found in
 the other documents in this directory and in the file
-Documentation/filesystems/sysfs.txt.
+Documentation/filesystems/sysfs.rst.
index d4e78cb3ef4d5c62e37bd8c1485158d4831bcc13..20c431c8e7be9b8dd5e3a1371dcb4fdd60f105b7 100644 (file)
@@ -39,6 +39,7 @@ available subsections can be seen below.
    spi
    i2c
    ipmb
+   ipmi
    i3c/index
    interconnect
    devfreq
index 08f855cbb4e61fc23ad57bb46c7be2ec344224cc..79c0fd39f2afc995cbf2453b347cad8b987d8067 100644 (file)
@@ -278,8 +278,8 @@ by a region device with a dynamically assigned id (REGION0 - REGION5).
        be contiguous in DPA-space.
 
     This bus is provided by the kernel under the device
-    /sys/devices/platform/nfit_test.0 when CONFIG_NFIT_TEST is enabled and
-    the nfit_test.ko module is loaded.  This not only test LIBNVDIMM but the
+    /sys/devices/platform/nfit_test.0 when the nfit_test.ko module from
+    tools/testing/nvdimm is loaded.  This not only test LIBNVDIMM but the
     acpi_nfit.ko driver as well.
 
 
index 006cf6db40c6d32c8a13b45930b1cdbc1d8f4c3e..3588bf078566946d9dda95e33597e15c97abd485 100644 (file)
@@ -68,9 +68,8 @@ only one in the list (that is, the list was empty before) or the value of its
 governor currently in use, or the name of the new governor was passed to the
 kernel as the value of the ``cpuidle.governor=`` command line parameter, the new
 governor will be used from that point on (there can be only one ``CPUIdle``
-governor in use at a time).  Also, if ``cpuidle_sysfs_switch`` is passed to the
-kernel in the command line, user space can choose the ``CPUIdle`` governor to
-use at run time via ``sysfs``.
+governor in use at a time).  Also, user space can choose the ``CPUIdle``
+governor to use at run time via ``sysfs``.
 
 Once registered, ``CPUIdle`` governors cannot be unregistered, so it is not
 practical to put them into loadable kernel modules.
index f66c7b9126ea5f824f27765022f8dd6a48939e05..946ad0b94e31ddf8fd6e9c59cc6619a937c871e5 100644 (file)
@@ -349,7 +349,7 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
        PM core will skip the ``suspend``, ``suspend_late`` and
        ``suspend_noirq`` phases as well as all of the corresponding phases of
        the subsequent device resume for all of these devices.  In that case,
-       the ``->complete`` callback will be invoked directly after the
+       the ``->complete`` callback will be the next one invoked after the
        ``->prepare`` callback and is entirely responsible for putting the
        device into a consistent state as appropriate.
 
@@ -361,9 +361,9 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
        runtime PM disabled.
 
        This feature also can be controlled by device drivers by using the
-       ``DPM_FLAG_NEVER_SKIP`` and ``DPM_FLAG_SMART_PREPARE`` driver power
-       management flags.  [Typically, they are set at the time the driver is
-       probed against the device in question by passing them to the
+       ``DPM_FLAG_NO_DIRECT_COMPLETE`` and ``DPM_FLAG_SMART_PREPARE`` driver
+       power management flags.  [Typically, they are set at the time the driver
+       is probed against the device in question by passing them to the
        :c:func:`dev_pm_set_driver_flags` helper function.]  If the first of
        these flags is set, the PM core will not apply the direct-complete
        procedure described above to the given device and, consequenty, to any
@@ -383,11 +383,15 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
        ``->suspend`` methods provided by subsystems (bus types and PM domains
        in particular) must follow an additional rule regarding what can be done
        to the devices before their drivers' ``->suspend`` methods are called.
-       Namely, they can only resume the devices from runtime suspend by
-       calling :c:func:`pm_runtime_resume` for them, if that is necessary, and
+       Namely, they may resume the devices from runtime suspend by
+       calling :c:func:`pm_runtime_resume` for them, if that is necessary, but
        they must not update the state of the devices in any other way at that
        time (in case the drivers need to resume the devices from runtime
-       suspend in their ``->suspend`` methods).
+       suspend in their ``->suspend`` methods).  In fact, the PM core prevents
+       subsystems or drivers from putting devices into runtime suspend at
+       these times by calling :c:func:`pm_runtime_get_noresume` before issuing
+       the ``->prepare`` callback (and calling :c:func:`pm_runtime_put` after
+       issuing the ``->complete`` callback).
 
     3. For a number of devices it is convenient to split suspend into the
        "quiesce device" and "save device state" phases, in which cases
@@ -459,22 +463,22 @@ When resuming from freeze, standby or memory sleep, the phases are:
 
        Note, however, that new children may be registered below the device as
        soon as the ``->resume`` callbacks occur; it's not necessary to wait
-       until the ``complete`` phase with that.
+       until the ``complete`` phase runs.
 
        Moreover, if the preceding ``->prepare`` callback returned a positive
        number, the device may have been left in runtime suspend throughout the
-       whole system suspend and resume (the ``suspend``, ``suspend_late``,
-       ``suspend_noirq`` phases of system suspend and the ``resume_noirq``,
-       ``resume_early``, ``resume`` phases of system resume may have been
-       skipped for it).  In that case, the ``->complete`` callback is entirely
+       whole system suspend and resume (its ``->suspend``, ``->suspend_late``,
+       ``->suspend_noirq``, ``->resume_noirq``,
+       ``->resume_early``, and ``->resume`` callbacks may have been
+       skipped).  In that case, the ``->complete`` callback is entirely
        responsible for putting the device into a consistent state after system
        suspend if necessary.  [For example, it may need to queue up a runtime
        resume request for the device for this purpose.]  To check if that is
        the case, the ``->complete`` callback can consult the device's
-       ``power.direct_complete`` flag.  Namely, if that flag is set when the
-       ``->complete`` callback is being run, it has been called directly after
-       the preceding ``->prepare`` and special actions may be required
-       to make the device work correctly afterward.
+       ``power.direct_complete`` flag.  If that flag is set when the
+       ``->complete`` callback is being run then the direct-complete mechanism
+       was used, and special actions may be required to make the device work
+       correctly afterward.
 
 At the end of these phases, drivers should be as functional as they were before
 suspending: I/O can be performed using DMA and IRQs, and the relevant clocks are
@@ -575,10 +579,12 @@ and the phases are similar.
 
 The ``->poweroff``, ``->poweroff_late`` and ``->poweroff_noirq`` callbacks
 should do essentially the same things as the ``->suspend``, ``->suspend_late``
-and ``->suspend_noirq`` callbacks, respectively.  The only notable difference is
+and ``->suspend_noirq`` callbacks, respectively.  A notable difference is
 that they need not store the device register values, because the registers
 should already have been stored during the ``freeze``, ``freeze_late`` or
-``freeze_noirq`` phases.
+``freeze_noirq`` phases.  Also, on many machines the firmware will power-down
+the entire system, so it is not necessary for the callback to put the device in
+a low-power state.
 
 
 Leaving Hibernation
@@ -764,70 +770,119 @@ device driver in question.
 
 If it is necessary to resume a device from runtime suspend during a system-wide
 transition into a sleep state, that can be done by calling
-:c:func:`pm_runtime_resume` for it from the ``->suspend`` callback (or its
-couterpart for transitions related to hibernation) of either the device's driver
-or a subsystem responsible for it (for example, a bus type or a PM domain).
-That is guaranteed to work by the requirement that subsystems must not change
-the state of devices (possibly except for resuming them from runtime suspend)
+:c:func:`pm_runtime_resume` from the ``->suspend`` callback (or the ``->freeze``
+or ``->poweroff`` callback for transitions related to hibernation) of either the
+device's driver or its subsystem (for example, a bus type or a PM domain).
+However, subsystems must not otherwise change the runtime status of devices
 from their ``->prepare`` and ``->suspend`` callbacks (or equivalent) *before*
 invoking device drivers' ``->suspend`` callbacks (or equivalent).
 
+.. _smart_suspend_flag:
+
+The ``DPM_FLAG_SMART_SUSPEND`` Driver Flag
+------------------------------------------
+
 Some bus types and PM domains have a policy to resume all devices from runtime
 suspend upfront in their ``->suspend`` callbacks, but that may not be really
-necessary if the driver of the device can cope with runtime-suspended devices.
-The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in
-:c:member:`power.driver_flags` at the probe time, by passing it to the
-:c:func:`dev_pm_set_driver_flags` helper.  That also may cause middle-layer code
+necessary if the device's driver can cope with runtime-suspended devices.
+The driver can indicate this by setting ``DPM_FLAG_SMART_SUSPEND`` in
+:c:member:`power.driver_flags` at probe time, with the assistance of the
+:c:func:`dev_pm_set_driver_flags` helper routine.
+
+Setting that flag causes the PM core and middle-layer code
 (bus types, PM domains etc.) to skip the ``->suspend_late`` and
 ``->suspend_noirq`` callbacks provided by the driver if the device remains in
-runtime suspend at the beginning of the ``suspend_late`` phase of system-wide
-suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM
-has been disabled for it, under the assumption that its state should not change
-after that point until the system-wide transition is over (the PM core itself
-does that for devices whose "noirq", "late" and "early" system-wide PM callbacks
-are executed directly by it).  If that happens, the driver's system-wide resume
-callbacks, if present, may still be invoked during the subsequent system-wide
-resume transition and the device's runtime power management status may be set
-to "active" before enabling runtime PM for it, so the driver must be prepared to
-cope with the invocation of its system-wide resume callbacks back-to-back with
-its ``->runtime_suspend`` one (without the intervening ``->runtime_resume`` and
-so on) and the final state of the device must reflect the "active" runtime PM
-status in that case.
+runtime suspend throughout those phases of the system-wide suspend (and
+similarly for the "freeze" and "poweroff" parts of system hibernation).
+[Otherwise the same driver
+callback might be executed twice in a row for the same device, which would not
+be valid in general.]  If the middle-layer system-wide PM callbacks are present
+for the device then they are responsible for skipping these driver callbacks;
+if not then the PM core skips them.  The subsystem callback routines can
+determine whether they need to skip the driver callbacks by testing the return
+value from the :c:func:`dev_pm_skip_suspend` helper function.
+
+In addition, with ``DPM_FLAG_SMART_SUSPEND`` set, the driver's ``->thaw_noirq``
+and ``->thaw_early`` callbacks are skipped in hibernation if the device remained
+in runtime suspend throughout the preceding "freeze" transition.  Again, if the
+middle-layer callbacks are present for the device, they are responsible for
+doing this, otherwise the PM core takes care of it.
+
+
+The ``DPM_FLAG_MAY_SKIP_RESUME`` Driver Flag
+--------------------------------------------
 
 During system-wide resume from a sleep state it's easiest to put devices into
 the full-power state, as explained in :file:`Documentation/power/runtime_pm.rst`.
 [Refer to that document for more information regarding this particular issue as
 well as for information on the device runtime power management framework in
-general.]
-
-However, it often is desirable to leave devices in suspend after system
-transitions to the working state, especially if those devices had been in
+general.]  However, it often is desirable to leave devices in suspend after
+system transitions to the working state, especially if those devices had been in
 runtime suspend before the preceding system-wide suspend (or analogous)
-transition.  Device drivers can use the ``DPM_FLAG_LEAVE_SUSPENDED`` flag to
-indicate to the PM core (and middle-layer code) that they prefer the specific
-devices handled by them to be left suspended and they have no problems with
-skipping their system-wide resume callbacks for this reason.  Whether or not the
-devices will actually be left in suspend may depend on their state before the
-given system suspend-resume cycle and on the type of the system transition under
-way.  In particular, devices are not left suspended if that transition is a
-restore from hibernation, as device states are not guaranteed to be reflected
-by the information stored in the hibernation image in that case.
-
-The middle-layer code involved in the handling of the device is expected to
-indicate to the PM core if the device may be left in suspend by setting its
-:c:member:`power.may_skip_resume` status bit which is checked by the PM core
-during the "noirq" phase of the preceding system-wide suspend (or analogous)
-transition.  The middle layer is then responsible for handling the device as
-appropriate in its "noirq" resume callback, which is executed regardless of
-whether or not the device is left suspended, but the other resume callbacks
-(except for ``->complete``) will be skipped automatically by the PM core if the
-device really can be left in suspend.
-
-For devices whose "noirq", "late" and "early" driver callbacks are invoked
-directly by the PM core, all of the system-wide resume callbacks are skipped if
-``DPM_FLAG_LEAVE_SUSPENDED`` is set and the device is in runtime suspend during
-the ``suspend_noirq`` (or analogous) phase or the transition under way is a
-proper system suspend (rather than anything related to hibernation) and the
-device's wakeup settings are suitable for runtime PM (that is, it cannot
-generate wakeup signals at all or it is allowed to wake up the system from
-sleep).
+transition.
+
+To that end, device drivers can use the ``DPM_FLAG_MAY_SKIP_RESUME`` flag to
+indicate to the PM core and middle-layer code that they allow their "noirq" and
+"early" resume callbacks to be skipped if the device can be left in suspend
+after system-wide PM transitions to the working state.  Whether or not that is
+the case generally depends on the state of the device before the given system
+suspend-resume cycle and on the type of the system transition under way.
+In particular, the "thaw" and "restore" transitions related to hibernation are
+not affected by ``DPM_FLAG_MAY_SKIP_RESUME`` at all.  [All callbacks are
+issued during the "restore" transition regardless of the flag settings,
+and whether or not any driver callbacks
+are skipped during the "thaw" transition depends whether or not the
+``DPM_FLAG_SMART_SUSPEND`` flag is set (see `above <smart_suspend_flag_>`_).
+In addition, a device is not allowed to remain in runtime suspend if any of its
+children will be returned to full power.]
+
+The ``DPM_FLAG_MAY_SKIP_RESUME`` flag is taken into account in combination with
+the :c:member:`power.may_skip_resume` status bit set by the PM core during the
+"suspend" phase of suspend-type transitions.  If the driver or the middle layer
+has a reason to prevent the driver's "noirq" and "early" resume callbacks from
+being skipped during the subsequent system resume transition, it should
+clear :c:member:`power.may_skip_resume` in its ``->suspend``, ``->suspend_late``
+or ``->suspend_noirq`` callback.  [Note that the drivers setting
+``DPM_FLAG_SMART_SUSPEND`` need to clear :c:member:`power.may_skip_resume` in
+their ``->suspend`` callback in case the other two are skipped.]
+
+Setting the :c:member:`power.may_skip_resume` status bit along with the
+``DPM_FLAG_MAY_SKIP_RESUME`` flag is necessary, but generally not sufficient,
+for the driver's "noirq" and "early" resume callbacks to be skipped.  Whether or
+not they should be skipped can be determined by evaluating the
+:c:func:`dev_pm_skip_resume` helper function.
+
+If that function returns ``true``, the driver's "noirq" and "early" resume
+callbacks should be skipped and the device's runtime PM status will be set to
+"suspended" by the PM core.  Otherwise, if the device was runtime-suspended
+during the preceding system-wide suspend transition and its
+``DPM_FLAG_SMART_SUSPEND`` is set, its runtime PM status will be set to
+"active" by the PM core.  [Hence, the drivers that do not set
+``DPM_FLAG_SMART_SUSPEND`` should not expect the runtime PM status of their
+devices to be changed from "suspended" to "active" by the PM core during
+system-wide resume-type transitions.]
+
+If the ``DPM_FLAG_MAY_SKIP_RESUME`` flag is not set for a device, but
+``DPM_FLAG_SMART_SUSPEND`` is set and the driver's "late" and "noirq" suspend
+callbacks are skipped, its system-wide "noirq" and "early" resume callbacks, if
+present, are invoked as usual and the device's runtime PM status is set to
+"active" by the PM core before enabling runtime PM for it.  In that case, the
+driver must be prepared to cope with the invocation of its system-wide resume
+callbacks back-to-back with its ``->runtime_suspend`` one (without the
+intervening ``->runtime_resume`` and system-wide suspend callbacks) and the
+final state of the device must reflect the "active" runtime PM status in that
+case.  [Note that this is not a problem at all if the driver's
+``->suspend_late`` callback pointer points to the same function as its
+``->runtime_suspend`` one and its ``->resume_early`` callback pointer points to
+the same function as the ``->runtime_resume`` one, while none of the other
+system-wide suspend-resume callbacks of the driver are present, for example.]
+
+Likewise, if ``DPM_FLAG_MAY_SKIP_RESUME`` is set for a device, its driver's
+system-wide "noirq" and "early" resume callbacks may be skipped while its "late"
+and "noirq" suspend callbacks may have been executed (in principle, regardless
+of whether or not ``DPM_FLAG_SMART_SUSPEND`` is set).  In that case, the driver
+needs to be able to cope with the invocation of its ``->runtime_resume``
+callback back-to-back with its "late" and "noirq" suspend ones.  [For instance,
+that is not a concern if the driver sets both ``DPM_FLAG_SMART_SUSPEND`` and
+``DPM_FLAG_MAY_SKIP_RESUME`` and uses the same pair of suspend/resume callback
+functions for runtime PM and system-wide suspend/resume.]
index a1c3edecae003e98c17a8becec6a156bd3a43ed7..b9f34ceb2a3899bf965914bd81c7bb82a096f1cb 100644 (file)
@@ -1,3 +1,6 @@
+================
+CPU Idle Cooling
+================
 
 Situation:
 ----------
index 5ba61d19c6aebd738b05a62af3044cb479e328e7..4cb0b9b6bfb8b6d0a006e51097fa9c9f5e5a9270 100644 (file)
@@ -8,6 +8,7 @@ Thermal
    :maxdepth: 1
 
    cpu-cooling-api
+   cpu-idle-cooling
    sysfs-api
    power_allocator
 
index 04840331a00e82d46276b1ce097dcbbb2ffe099f..6badff64756f4976201a4beca7da209e4e2d9551 100644 (file)
@@ -2,8 +2,10 @@
 What is efifb?
 ==============
 
-This is a generic EFI platform driver for Intel based Apple computers.
-efifb is only for EFI booted Intel Macs.
+This is a generic EFI platform driver for systems with UEFI firmware. The
+system must be booted via the EFI stub for this to be usable. efifb supports
+both firmware with Graphics Output Protocol (GOP) displays as well as older
+systems with only Universal Graphics Adapter (UGA) displays.
 
 Supported Hardware
 ==================
@@ -12,11 +14,14 @@ Supported Hardware
 - Macbook
 - Macbook Pro 15"/17"
 - MacMini
+- ARM/ARM64/X86 systems with UEFI firmware
 
 How to use it?
 ==============
 
-efifb does not have any kind of autodetection of your machine.
+For UGA displays, efifb does not have any kind of autodetection of your
+machine.
+
 You have to add the following kernel parameters in your elilo.conf::
 
        Macbook :
@@ -28,6 +33,9 @@ You have to add the following kernel parameters in your elilo.conf::
        Macbook Pro 17", iMac 20" :
                video=efifb:i20
 
+For GOP displays, efifb can autodetect the display's resolution and framebuffer
+address, so these should work out of the box without any special parameters.
+
 Accepted options:
 
 ======= ===========================================================
@@ -36,4 +44,28 @@ nowc Don't map the framebuffer write combined. This can be used
        when large amounts of console data are written.
 ======= ===========================================================
 
+Options for GOP displays:
+
+mode=n
+        The EFI stub will set the mode of the display to mode number n if
+        possible.
+
+<xres>x<yres>[-(rgb|bgr|<bpp>)]
+        The EFI stub will search for a display mode that matches the specified
+        horizontal and vertical resolution, and optionally bit depth, and set
+        the mode of the display to it if one is found. The bit depth can either
+        "rgb" or "bgr" to match specifically those pixel formats, or a number
+        for a mode with matching bits per pixel.
+
+auto
+        The EFI stub will choose the mode with the highest resolution (product
+        of horizontal and vertical resolution). If there are multiple modes
+        with the highest resolution, it will choose one with the highest color
+        depth.
+
+list
+        The EFI stub will list out all the display modes that are available. A
+        specific mode can then be chosen using one of the above options for the
+        next boot.
+
 Edgar Hucek <gimli@dark-green.com>
index 9ae6e8d0d10de05d5e238fdc6ace62e89ccec823..9ed964f652241ec14b9e3f4b993495396e44f583 100644 (file)
@@ -23,7 +23,7 @@
     |    openrisc: | TODO |
     |      parisc: | TODO |
     |     powerpc: |  ok  |
-    |       riscv: | TODO |
+    |       riscv: |  ok  |
     |        s390: |  ok  |
     |          sh: | TODO |
     |       sparc: |  ok  |
index 304dcd4617958cdb8509b0457a849fa89d796fa3..6ff38548923e1432517b574d71fe42ef19494715 100644 (file)
@@ -22,9 +22,9 @@
     |       nios2: | TODO |
     |    openrisc: | TODO |
     |      parisc: | TODO |
-    |     powerpc: | TODO |
-    |       riscv: | TODO |
-    |        s390: | TODO |
+    |     powerpc: |  ok  |
+    |       riscv: |  ok  |
+    |        s390: |  ok  |
     |          sh: | TODO |
     |       sparc: | TODO |
     |          um: | TODO |
index 6fb2b0671994efd5d526762e58ef6f2b242a1276..210256f6a4cfed932d5ef160a3d09ded88a4d1a3 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
index 32b297295fff79f5da38b7d291fa8fb1f3fe96d9..97cd7aa749054384b9fc2b4f4921e113692bf77a 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: | TODO |
     |       arm64: | TODO |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
index e68239b5d2f08bf149075ec742e3eb15e441d2b4..8b316c6e03d4b8f9861105a43cb1759e524ed6d2 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: |  ok  |
@@ -23,7 +23,7 @@
     |    openrisc: | TODO |
     |      parisc: |  ok  |
     |     powerpc: |  ok  |
-    |       riscv: |  ok  |
+    |       riscv: | TODO |
     |        s390: |  ok  |
     |          sh: |  ok  |
     |       sparc: |  ok  |
index f17131b328e51b5413c5cb25c8bba4e11d1ff678..b805aada395e81ba2c06d7d073e484af98fe8009 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: |  ok  |
index 32bbdfc64c32ac4e686f71d185eb318beb89cd0b..12410f606edc22ca5e6438e0e4da76c6bc406063 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
index 1c577d0cfc7ff5a80ae9ca98f2b40c3542dfc8c2..be8acbb95b5426ae1b1fe6f264dec1cc8306a92e 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
index eb28b5c97ca6b6a540c2f7fc369fe44c59e67d42..895c3b0f649220c1e169d20658b82d89e13f5943 100644 (file)
@@ -16,7 +16,7 @@
     |     hexagon: | TODO |
     |        ia64: | TODO |
     |        m68k: | TODO |
-    |  microblaze: | TODO |
+    |  microblaze: |  ok  |
     |        mips: |  ok  |
     |       nds32: | TODO |
     |       nios2: | TODO |
index 941fd5b1094dcea5086afccbf959cb7170d60354..98cb9d85c55d4212c35cedde07272d0475d86ed4 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: |  ok  |
     |        ia64: | TODO |
index d8278bf62b85532ffe2e1b4f93362759e82358bd..518f352fc7271485baeb9378458a313e7eba93d1 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: |  ok  |
     |        ia64: | TODO |
@@ -21,7 +21,7 @@
     |       nds32: |  ok  |
     |       nios2: | TODO |
     |    openrisc: | TODO |
-    |      parisc: | TODO |
+    |      parisc: |  ok  |
     |     powerpc: |  ok  |
     |       riscv: | TODO |
     |        s390: |  ok  |
index 687d049d9cee73bc27a193b118889ece3fd057cf..c22cd6f8aa5e896a80c85adbbe199f300775b76f 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
@@ -23,7 +23,7 @@
     |    openrisc: | TODO |
     |      parisc: | TODO |
     |     powerpc: |  ok  |
-    |       riscv: | TODO |
+    |       riscv: |  ok  |
     |        s390: |  ok  |
     |          sh: | TODO |
     |       sparc: | TODO |
index 90996e3d18a89d3a6eb4eb05021cc18afb4d9aed..527fe4d0b0746f63619432006b725945b3646e72 100644 (file)
@@ -11,7 +11,7 @@
     |         arm: |  ok  |
     |       arm64: |  ok  |
     |         c6x: | TODO |
-    |        csky: | TODO |
+    |        csky: |  ok  |
     |       h8300: | TODO |
     |     hexagon: | TODO |
     |        ia64: | TODO |
@@ -23,7 +23,7 @@
     |    openrisc: | TODO |
     |      parisc: | TODO |
     |     powerpc: |  ok  |
-    |       riscv: | TODO |
+    |       riscv: |  ok  |
     |        s390: |  ok  |
     |          sh: | TODO |
     |       sparc: | TODO |
index 4fe6c3c3be5c91a9e77578593a978d10cab25a40..c7b837f735b174e6d3521dfef78a229b290f678c 100644 (file)
@@ -23,7 +23,7 @@
     |    openrisc: | TODO |
     |      parisc: |  ok  |
     |     powerpc: |  ok  |
-    |       riscv: | TODO |
+    |       riscv: |  ok  |
     |        s390: |  ok  |
     |          sh: | TODO |
     |       sparc: | TODO |
index 019131c5acce9d6f8be7b3cdacce8d99f74d3a95..8525f1981f19b99b73d7d47f0d487b6907256bdb 100644 (file)
@@ -22,7 +22,7 @@
     |       nios2: | TODO |
     |    openrisc: | TODO |
     |      parisc: | TODO |
-    |     powerpc: | TODO |
+    |     powerpc: |  ok  |
     |       riscv: | TODO |
     |        s390: | TODO |
     |          sh: | TODO |
index 3d492a34c8ee3fcb4d303f9b052831104e75cf44..2e017387e228baf4fae17057bfeea2dddd10e5c3 100644 (file)
@@ -17,7 +17,7 @@
     |        ia64: | TODO |
     |        m68k: | TODO |
     |  microblaze: | TODO |
-    |        mips: | TODO |
+    |        mips: |  ok  |
     |       nds32: | TODO |
     |       nios2: | TODO |
     |    openrisc: | TODO |
index 671fef39a8028cb39b17fbd4c1b6ba252d91cd13..2995279ddc24d6889d2dd655f343e41755bb5e5f 100644 (file)
@@ -192,4 +192,4 @@ For more information on the Plan 9 Operating System check out
 http://plan9.bell-labs.com/plan9
 
 For information on Plan 9 from User Space (Plan 9 applications and libraries
-ported to Linux/BSD/OSX/etc) check out http://swtch.com/plan9
+ported to Linux/BSD/OSX/etc) check out https://9fans.github.io/plan9port/
similarity index 92%
rename from Documentation/filesystems/automount-support.txt
rename to Documentation/filesystems/automount-support.rst
index 7d9f82607562713a83500d0dab9ea82c6810937f..430f0b40796bef7cb5b4f569621ea3bb760c62d4 100644 (file)
@@ -1,3 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+Automount Support
+=================
+
+
 Support is available for filesystems that wish to do automounting
 support (such as kAFS which can be found in fs/afs/ and NFS in
 fs/nfs/). This facility includes allowing in-kernel mounts to be
@@ -5,13 +12,12 @@ performed and mountpoint degradation to be requested. The latter can
 also be requested by userspace.
 
 
-======================
-IN-KERNEL AUTOMOUNTING
+In-Kernel Automounting
 ======================
 
 See section "Mount Traps" of  Documentation/filesystems/autofs.rst
 
-Then from userspace, you can just do something like:
+Then from userspace, you can just do something like::
 
        [root@andromeda root]# mount -t afs \#root.afs. /afs
        [root@andromeda root]# ls /afs
@@ -21,7 +27,7 @@ Then from userspace, you can just do something like:
        [root@andromeda root]# ls /afs/cambridge/afsdoc/
        ChangeLog  html  LICENSE  pdf  RELNOTES-1.2.2
 
-And then if you look in the mountpoint catalogue, you'll see something like:
+And then if you look in the mountpoint catalogue, you'll see something like::
 
        [root@andromeda root]# cat /proc/mounts
        ...
@@ -30,8 +36,7 @@ And then if you look in the mountpoint catalogue, you'll see something like:
        #afsdoc. /afs/cambridge.redhat.com/afsdoc afs rw 0 0
 
 
-===========================
-AUTOMATIC MOUNTPOINT EXPIRY
+Automatic Mountpoint Expiry
 ===========================
 
 Automatic expiration of mountpoints is easy, provided you've mounted the
@@ -43,7 +48,8 @@ To do expiration, you need to follow these steps:
      hung.
 
  (2) When a new mountpoint is created in the ->d_automount method, add
-     the mnt to the list using mnt_set_expiry()
+     the mnt to the list using mnt_set_expiry()::
+
              mnt_set_expiry(newmnt, &afs_vfsmounts);
 
  (3) When you want mountpoints to be expired, call mark_mounts_for_expiry()
@@ -70,8 +76,7 @@ and the copies of those that are on an expiration list will be added to the
 same expiration list.
 
 
-=======================
-USERSPACE DRIVEN EXPIRY
+Userspace Driven Expiry
 =======================
 
 As an alternative, it is possible for userspace to request expiry of any
similarity index 87%
rename from Documentation/filesystems/caching/backend-api.txt
rename to Documentation/filesystems/caching/backend-api.rst
index c418280c915fb167424cb7600d6350cbebf44655..19fbf6b9aa36005dbc38630b030bf4357a23d5bf 100644 (file)
@@ -1,6 +1,8 @@
-                         ==========================
-                         FS-CACHE CACHE BACKEND API
-                         ==========================
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+FS-Cache Cache backend API
+==========================
 
 The FS-Cache system provides an API by which actual caches can be supplied to
 FS-Cache for it to then serve out to network filesystems and other interested
@@ -9,15 +11,14 @@ parties.
 This API is declared in <linux/fscache-cache.h>.
 
 
-====================================
-INITIALISING AND REGISTERING A CACHE
+Initialising and Registering a Cache
 ====================================
 
 To start off, a cache definition must be initialised and registered for each
 cache the backend wants to make available.  For instance, CacheFS does this in
 the fill_super() operation on mounting.
 
-The cache definition (struct fscache_cache) should be initialised by calling:
+The cache definition (struct fscache_cache) should be initialised by calling::
 
        void fscache_init_cache(struct fscache_cache *cache,
                                struct fscache_cache_ops *ops,
@@ -26,17 +27,17 @@ The cache definition (struct fscache_cache) should be initialised by calling:
 
 Where:
 
(*) "cache" is a pointer to the cache definition;
  * "cache" is a pointer to the cache definition;
 
(*) "ops" is a pointer to the table of operations that the backend supports on
  * "ops" is a pointer to the table of operations that the backend supports on
      this cache; and
 
(*) "idfmt" is a format and printf-style arguments for constructing a label
  * "idfmt" is a format and printf-style arguments for constructing a label
      for the cache.
 
 
 The cache should then be registered with FS-Cache by passing a pointer to the
-previously initialised cache definition to:
+previously initialised cache definition to::
 
        int fscache_add_cache(struct fscache_cache *cache,
                              struct fscache_object *fsdef,
@@ -44,12 +45,12 @@ previously initialised cache definition to:
 
 Two extra arguments should also be supplied:
 
(*) "fsdef" which should point to the object representation for the FS-Cache
  * "fsdef" which should point to the object representation for the FS-Cache
      master index in this cache.  Netfs primary index entries will be created
      here.  FS-Cache keeps the caller's reference to the index object if
      successful and will release it upon withdrawal of the cache.
 
(*) "tagname" which, if given, should be a text string naming this cache.  If
  * "tagname" which, if given, should be a text string naming this cache.  If
      this is NULL, the identifier will be used instead.  For CacheFS, the
      identifier is set to name the underlying block device and the tag can be
      supplied by mount.
@@ -58,20 +59,18 @@ This function may return -ENOMEM if it ran out of memory or -EEXIST if the tag
 is already in use.  0 will be returned on success.
 
 
-=====================
-UNREGISTERING A CACHE
+Unregistering a Cache
 =====================
 
 A cache can be withdrawn from the system by calling this function with a
-pointer to the cache definition:
+pointer to the cache definition::
 
        void fscache_withdraw_cache(struct fscache_cache *cache);
 
 In CacheFS's case, this is called by put_super().
 
 
-========
-SECURITY
+Security
 ========
 
 The cache methods are executed one of two contexts:
@@ -89,8 +88,7 @@ be masqueraded for the duration of the cache driver's access to the cache.
 This is left to the cache to handle; FS-Cache makes no effort in this regard.
 
 
-===================================
-CONTROL AND STATISTICS PRESENTATION
+Control and Statistics Presentation
 ===================================
 
 The cache may present data to the outside world through FS-Cache's interfaces
@@ -101,11 +99,10 @@ is enabled.  This is accessible through the kobject struct fscache_cache::kobj
 and is for use by the cache as it sees fit.
 
 
-========================
-RELEVANT DATA STRUCTURES
+Relevant Data Structures
 ========================
 
(*) Index/Data file FS-Cache representation cookie:
  * Index/Data file FS-Cache representation cookie::
 
        struct fscache_cookie {
                struct fscache_object_def       *def;
@@ -121,7 +118,7 @@ RELEVANT DATA STRUCTURES
      cache operations.
 
 
(*) In-cache object representation:
  * In-cache object representation::
 
        struct fscache_object {
                int                             debug_id;
@@ -150,7 +147,7 @@ RELEVANT DATA STRUCTURES
      initialised by calling fscache_object_init(object).
 
 
(*) FS-Cache operation record:
  * FS-Cache operation record::
 
        struct fscache_operation {
                atomic_t                usage;
@@ -173,7 +170,7 @@ RELEVANT DATA STRUCTURES
      an operation needs more processing time, it should be enqueued again.
 
 
(*) FS-Cache retrieval operation record:
  * FS-Cache retrieval operation record::
 
        struct fscache_retrieval {
                struct fscache_operation op;
@@ -198,7 +195,7 @@ RELEVANT DATA STRUCTURES
      it sees fit.
 
 
(*) FS-Cache storage operation record:
  * FS-Cache storage operation record::
 
        struct fscache_storage {
                struct fscache_operation op;
@@ -212,16 +209,17 @@ RELEVANT DATA STRUCTURES
      storage.
 
 
-================
-CACHE OPERATIONS
+Cache Operations
 ================
 
 The cache backend provides FS-Cache with a table of operations that can be
 performed on the denizens of the cache.  These are held in a structure of type:
 
-       struct fscache_cache_ops
+       ::
+
+           struct fscache_cache_ops
 
(*) Name of cache provider [mandatory]:
  * Name of cache provider [mandatory]::
 
        const char *name
 
@@ -229,7 +227,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      the backend.
 
 
(*) Allocate a new object [mandatory]:
  * Allocate a new object [mandatory]::
 
        struct fscache_object *(*alloc_object)(struct fscache_cache *cache,
                                               struct fscache_cookie *cookie)
@@ -244,7 +242,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      form once lookup is complete or aborted.
 
 
(*) Look up and create object [mandatory]:
  * Look up and create object [mandatory]::
 
        void (*lookup_object)(struct fscache_object *object)
 
@@ -263,7 +261,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      to abort the lookup of that object.
 
 
(*) Release lookup data [mandatory]:
  * Release lookup data [mandatory]::
 
        void (*lookup_complete)(struct fscache_object *object)
 
@@ -271,7 +269,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      using to perform a lookup.
 
 
(*) Increment object refcount [mandatory]:
  * Increment object refcount [mandatory]::
 
        struct fscache_object *(*grab_object)(struct fscache_object *object)
 
@@ -280,7 +278,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      It should return the object pointer if successful.
 
 
(*) Lock/Unlock object [mandatory]:
  * Lock/Unlock object [mandatory]::
 
        void (*lock_object)(struct fscache_object *object)
        void (*unlock_object)(struct fscache_object *object)
@@ -289,7 +287,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      to schedule with the lock held, so a spinlock isn't sufficient.
 
 
(*) Pin/Unpin object [optional]:
  * Pin/Unpin object [optional]::
 
        int (*pin_object)(struct fscache_object *object)
        void (*unpin_object)(struct fscache_object *object)
@@ -299,7 +297,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      enough space in the cache to permit this.
 
 
(*) Check coherency state of an object [mandatory]:
  * Check coherency state of an object [mandatory]::
 
        int (*check_consistency)(struct fscache_object *object)
 
@@ -308,7 +306,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      if they're consistent and -ESTALE otherwise.  -ENOMEM and -ERESTARTSYS
      may also be returned.
 
(*) Update object [mandatory]:
  * Update object [mandatory]::
 
        int (*update_object)(struct fscache_object *object)
 
@@ -317,7 +315,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      obtained by calling object->cookie->def->get_aux()/get_attr().
 
 
(*) Invalidate data object [mandatory]:
  * Invalidate data object [mandatory]::
 
        int (*invalidate_object)(struct fscache_operation *op)
 
@@ -329,7 +327,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      fscache_op_complete() must be called on op before returning.
 
 
(*) Discard object [mandatory]:
  * Discard object [mandatory]::
 
        void (*drop_object)(struct fscache_object *object)
 
@@ -341,7 +339,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      caller.  The caller will invoke the put_object() method as appropriate.
 
 
(*) Release object reference [mandatory]:
  * Release object reference [mandatory]::
 
        void (*put_object)(struct fscache_object *object)
 
@@ -349,7 +347,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      be freed when all the references to it are released.
 
 
(*) Synchronise a cache [mandatory]:
  * Synchronise a cache [mandatory]::
 
        void (*sync)(struct fscache_cache *cache)
 
@@ -357,7 +355,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      device.
 
 
(*) Dissociate a cache [mandatory]:
  * Dissociate a cache [mandatory]::
 
        void (*dissociate_pages)(struct fscache_cache *cache)
 
@@ -365,7 +363,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      cache withdrawal.
 
 
(*) Notification that the attributes on a netfs file changed [mandatory]:
  * Notification that the attributes on a netfs file changed [mandatory]::
 
        int (*attr_changed)(struct fscache_object *object);
 
@@ -386,7 +384,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      execution of this operation.
 
 
(*) Reserve cache space for an object's data [optional]:
  * Reserve cache space for an object's data [optional]::
 
        int (*reserve_space)(struct fscache_object *object, loff_t size);
 
@@ -404,7 +402,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      size if larger than that already.
 
 
(*) Request page be read from cache [mandatory]:
  * Request page be read from cache [mandatory]::
 
        int (*read_or_alloc_page)(struct fscache_retrieval *op,
                                  struct page *page,
@@ -446,7 +444,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      with.  This will complete the operation when all pages are dealt with.
 
 
(*) Request pages be read from cache [mandatory]:
  * Request pages be read from cache [mandatory]::
 
        int (*read_or_alloc_pages)(struct fscache_retrieval *op,
                                   struct list_head *pages,
@@ -457,7 +455,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      of pages instead of one page.  Any pages on which a read operation is
      started must be added to the page cache for the specified mapping and also
      to the LRU.  Such pages must also be removed from the pages list and
-     *nr_pages decremented per page.
+     ``*nr_pages`` decremented per page.
 
      If there was an error such as -ENOMEM, then that should be returned; else
      if one or more pages couldn't be read or allocated, then -ENOBUFS should
@@ -466,7 +464,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      returned.
 
 
(*) Request page be allocated in the cache [mandatory]:
  * Request page be allocated in the cache [mandatory]::
 
        int (*allocate_page)(struct fscache_retrieval *op,
                             struct page *page,
@@ -482,7 +480,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      allocated, then the netfs page should be marked and 0 returned.
 
 
(*) Request pages be allocated in the cache [mandatory]:
  * Request pages be allocated in the cache [mandatory]::
 
        int (*allocate_pages)(struct fscache_retrieval *op,
                              struct list_head *pages,
@@ -493,7 +491,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      nr_pages should be treated as for the read_or_alloc_pages() method.
 
 
(*) Request page be written to cache [mandatory]:
  * Request page be written to cache [mandatory]::
 
        int (*write_page)(struct fscache_storage *op,
                          struct page *page);
@@ -514,7 +512,7 @@ performed on the denizens of the cache.  These are held in a structure of type:
      appropriately.
 
 
(*) Discard retained per-page metadata [mandatory]:
  * Discard retained per-page metadata [mandatory]::
 
        void (*uncache_page)(struct fscache_object *object, struct page *page)
 
@@ -523,13 +521,12 @@ performed on the denizens of the cache.  These are held in a structure of type:
      maintains for this page.
 
 
-==================
-FS-CACHE UTILITIES
+FS-Cache Utilities
 ==================
 
 FS-Cache provides some utilities that a cache backend may make use of:
 
(*) Note occurrence of an I/O error in a cache:
  * Note occurrence of an I/O error in a cache::
 
        void fscache_io_error(struct fscache_cache *cache)
 
@@ -541,7 +538,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      This does not actually withdraw the cache.  That must be done separately.
 
 
(*) Invoke the retrieval I/O completion function:
  * Invoke the retrieval I/O completion function::
 
        void fscache_end_io(struct fscache_retrieval *op, struct page *page,
                            int error);
@@ -550,8 +547,8 @@ FS-Cache provides some utilities that a cache backend may make use of:
      error value should be 0 if successful and an error otherwise.
 
 
(*) Record that one or more pages being retrieved or allocated have been dealt
-     with:
  * Record that one or more pages being retrieved or allocated have been dealt
+     with::
 
        void fscache_retrieval_complete(struct fscache_retrieval *op,
                                        int n_pages);
@@ -562,7 +559,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      completed.
 
 
(*) Record operation completion:
  * Record operation completion::
 
        void fscache_op_complete(struct fscache_operation *op);
 
@@ -571,7 +568,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      one or more pending operations to start running.
 
 
(*) Set highest store limit:
  * Set highest store limit::
 
        void fscache_set_store_limit(struct fscache_object *object,
                                     loff_t i_size);
@@ -581,7 +578,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      rejected by fscache_read_alloc_page() and co with -ENOBUFS.
 
 
(*) Mark pages as being cached:
  * Mark pages as being cached::
 
        void fscache_mark_pages_cached(struct fscache_retrieval *op,
                                       struct pagevec *pagevec);
@@ -590,7 +587,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      the netfs must call fscache_uncache_page() to unmark the pages.
 
 
(*) Perform coherency check on an object:
  * Perform coherency check on an object::
 
        enum fscache_checkaux fscache_check_aux(struct fscache_object *object,
                                                const void *data,
@@ -603,29 +600,26 @@ FS-Cache provides some utilities that a cache backend may make use of:
 
      One of three values will be returned:
 
-       (*) FSCACHE_CHECKAUX_OKAY
-
+       FSCACHE_CHECKAUX_OKAY
            The coherency data indicates the object is valid as is.
 
-       (*) FSCACHE_CHECKAUX_NEEDS_UPDATE
-
+       FSCACHE_CHECKAUX_NEEDS_UPDATE
            The coherency data needs updating, but otherwise the object is
            valid.
 
-       (*) FSCACHE_CHECKAUX_OBSOLETE
-
+       FSCACHE_CHECKAUX_OBSOLETE
            The coherency data indicates that the object is obsolete and should
            be discarded.
 
 
(*) Initialise a freshly allocated object:
  * Initialise a freshly allocated object::
 
        void fscache_object_init(struct fscache_object *object);
 
      This initialises all the fields in an object representation.
 
 
(*) Indicate the destruction of an object:
  * Indicate the destruction of an object::
 
        void fscache_object_destroyed(struct fscache_cache *cache);
 
@@ -635,7 +629,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      all the objects.
 
 
(*) Indicate negative lookup on an object:
  * Indicate negative lookup on an object::
 
        void fscache_object_lookup_negative(struct fscache_object *object);
 
@@ -650,7 +644,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      significant - all subsequent calls are ignored.
 
 
(*) Indicate an object has been obtained:
  * Indicate an object has been obtained::
 
        void fscache_obtained_object(struct fscache_object *object);
 
@@ -667,7 +661,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
         (2) that writes may now proceed against this object.
 
 
(*) Indicate that object lookup failed:
  * Indicate that object lookup failed::
 
        void fscache_object_lookup_error(struct fscache_object *object);
 
@@ -676,7 +670,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      as possible.
 
 
(*) Indicate that a stale object was found and discarded:
  * Indicate that a stale object was found and discarded::
 
        void fscache_object_retrying_stale(struct fscache_object *object);
 
@@ -685,7 +679,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      discarded from the cache and the lookup will be performed again.
 
 
(*) Indicate that the caching backend killed an object:
  * Indicate that the caching backend killed an object::
 
        void fscache_object_mark_killed(struct fscache_object *object,
                                        enum fscache_why_object_killed why);
@@ -693,13 +687,20 @@ FS-Cache provides some utilities that a cache backend may make use of:
      This is called to indicate that the cache backend preemptively killed an
      object.  The why parameter should be set to indicate the reason:
 
-       FSCACHE_OBJECT_IS_STALE - the object was stale and needs discarding.
-       FSCACHE_OBJECT_NO_SPACE - there was insufficient cache space
-       FSCACHE_OBJECT_WAS_RETIRED - the object was retired when relinquished.
-       FSCACHE_OBJECT_WAS_CULLED - the object was culled to make space.
+       FSCACHE_OBJECT_IS_STALE
+           - the object was stale and needs discarding.
+
+       FSCACHE_OBJECT_NO_SPACE
+           - there was insufficient cache space
+
+       FSCACHE_OBJECT_WAS_RETIRED
+           - the object was retired when relinquished.
+
+       FSCACHE_OBJECT_WAS_CULLED
+           - the object was culled to make space.
 
 
(*) Get and release references on a retrieval record:
  * Get and release references on a retrieval record::
 
        void fscache_get_retrieval(struct fscache_retrieval *op);
        void fscache_put_retrieval(struct fscache_retrieval *op);
@@ -708,7 +709,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      asynchronous data retrieval and block allocation.
 
 
- (*) Enqueue a retrieval record for processing.
+   * Enqueue a retrieval record for processing::
 
        void fscache_enqueue_retrieval(struct fscache_retrieval *op);
 
@@ -718,7 +719,7 @@ FS-Cache provides some utilities that a cache backend may make use of:
      within the callback function.
 
 
(*) List of object state names:
  * List of object state names::
 
        const char *fscache_object_states[];
 
similarity index 90%
rename from Documentation/filesystems/caching/cachefiles.txt
rename to Documentation/filesystems/caching/cachefiles.rst
index 28aefcbb14425f093d6d75e41482425c329780aa..65d3db47676566450092cde236b8d4a85be32144 100644 (file)
@@ -1,8 +1,10 @@
-              ===============================================
-              CacheFiles: CACHE ON ALREADY MOUNTED FILESYSTEM
-              ===============================================
+.. SPDX-License-Identifier: GPL-2.0
 
-Contents:
+===============================================
+CacheFiles: CACHE ON ALREADY MOUNTED FILESYSTEM
+===============================================
+
+.. Contents:
 
  (*) Overview.
 
@@ -27,8 +29,8 @@ Contents:
  (*) Debugging.
 
 
-========
-OVERVIEW
+
+Overview
 ========
 
 CacheFiles is a caching backend that's meant to use as a cache a directory on
@@ -58,8 +60,8 @@ spare space and automatically contract when the set of data requires more
 space.
 
 
-============
-REQUIREMENTS
+
+Requirements
 ============
 
 The use of CacheFiles and its daemon requires the following features to be
@@ -79,84 +81,70 @@ It is strongly recommended that the "dir_index" option is enabled on Ext3
 filesystems being used as a cache.
 
 
-=============
-CONFIGURATION
+Configuration
 =============
 
 The cache is configured by a script in /etc/cachefilesd.conf.  These commands
 set up cache ready for use.  The following script commands are available:
 
- (*) brun <N>%
- (*) bcull <N>%
- (*) bstop <N>%
- (*) frun <N>%
- (*) fcull <N>%
- (*) fstop <N>%
-
+ brun <N>%, bcull <N>%, bstop <N>%, frun <N>%, fcull <N>%, fstop <N>%
        Configure the culling limits.  Optional.  See the section on culling
        The defaults are 7% (run), 5% (cull) and 1% (stop) respectively.
 
        The commands beginning with a 'b' are file space (block) limits, those
        beginning with an 'f' are file count limits.
 
- (*) dir <path>
-
+ dir <path>
        Specify the directory containing the root of the cache.  Mandatory.
 
- (*) tag <name>
-
+ tag <name>
        Specify a tag to FS-Cache to use in distinguishing multiple caches.
        Optional.  The default is "CacheFiles".
 
- (*) debug <mask>
-
+ debug <mask>
        Specify a numeric bitmask to control debugging in the kernel module.
        Optional.  The default is zero (all off).  The following values can be
        OR'd into the mask to collect various information:
 
+               ==      =================================================
                1       Turn on trace of function entry (_enter() macros)
                2       Turn on trace of function exit (_leave() macros)
                4       Turn on trace of internal debug points (_debug())
+               ==      =================================================
 
-       This mask can also be set through sysfs, eg:
+       This mask can also be set through sysfs, eg::
 
                echo 5 >/sys/modules/cachefiles/parameters/debug
 
 
-==================
-STARTING THE CACHE
+Starting the Cache
 ==================
 
 The cache is started by running the daemon.  The daemon opens the cache device,
 configures the cache and tells it to begin caching.  At that point the cache
 binds to fscache and the cache becomes live.
 
-The daemon is run as follows:
+The daemon is run as follows::
 
        /sbin/cachefilesd [-d]* [-s] [-n] [-f <configfile>]
 
 The flags are:
 
- (*) -d
-
+ ``-d``
        Increase the debugging level.  This can be specified multiple times and
        is cumulative with itself.
 
- (*) -s
-
+ ``-s``
        Send messages to stderr instead of syslog.
 
- (*) -n
-
+ ``-n``
        Don't daemonise and go into background.
 
- (*) -f <configfile>
-
+ ``-f <configfile>``
        Use an alternative configuration file rather than the default one.
 
 
-===============
-THINGS TO AVOID
+Things to Avoid
 ===============
 
 Do not mount other things within the cache as this will cause problems.  The
@@ -179,8 +167,7 @@ Do not chmod files in the cache.  The module creates things with minimal
 permissions to prevent random users being able to access them directly.
 
 
-=============
-CACHE CULLING
+Cache Culling
 =============
 
 The cache may need culling occasionally to make space.  This involves
@@ -192,27 +179,21 @@ Cache culling is done on the basis of the percentage of blocks and the
 percentage of files available in the underlying filesystem.  There are six
 "limits":
 
- (*) brun
- (*) frun
-
+ brun, frun
      If the amount of free space and the number of available files in the cache
      rises above both these limits, then culling is turned off.
 
- (*) bcull
- (*) fcull
-
+ bcull, fcull
      If the amount of available space or the number of available files in the
      cache falls below either of these limits, then culling is started.
 
- (*) bstop
- (*) fstop
-
+ bstop, fstop
      If the amount of available space or the number of available files in the
      cache falls below either of these limits, then no further allocation of
      disk space or files is permitted until culling has raised things above
      these limits again.
 
-These must be configured thusly:
+These must be configured thusly::
 
        0 <= bstop < bcull < brun < 100
        0 <= fstop < fcull < frun < 100
@@ -226,16 +207,14 @@ started as soon as space is made in the table.  Objects will be skipped if
 their atimes have changed or if the kernel module says it is still using them.
 
 
-===============
-CACHE STRUCTURE
+Cache Structure
 ===============
 
 The CacheFiles module will create two directories in the directory it was
 given:
 
- (*) cache/
-
- (*) graveyard/
+ * cache/
+ * graveyard/
 
 The active cache objects all reside in the first directory.  The CacheFiles
 kernel module moves any retired or culled objects that it can't simply unlink
@@ -261,10 +240,10 @@ If an object has children, then it will be represented as a directory.
 Immediately in the representative directory are a collection of directories
 named for hash values of the child object keys with an '@' prepended.  Into
 this directory, if possible, will be placed the representations of the child
-objects:
+objects::
 
-       INDEX     INDEX      INDEX                             DATA FILES
-       ========= ========== ================================= ================
+        /INDEX    /INDEX     /INDEX                            /DATA FILES
+       /=========/==========/=================================/================
        cache/@4a/I03nfs/@30/Ji000000000000000--fHg8hi8400
        cache/@4a/I03nfs/@30/Ji000000000000000--fHg8hi8400/@75/Es0g000w...DB1ry
        cache/@4a/I03nfs/@30/Ji000000000000000--fHg8hi8400/@75/Es0g000w...N22ry
@@ -275,7 +254,7 @@ If the key is so long that it exceeds NAME_MAX with the decorations added on to
 it, then it will be cut into pieces, the first few of which will be used to
 make a nest of directories, and the last one of which will be the objects
 inside the last directory.  The names of the intermediate directories will have
-'+' prepended:
+'+' prepended::
 
        J1223/@23/+xy...z/+kl...m/Epqr
 
@@ -288,11 +267,13 @@ To handle this, CacheFiles will use a suitably printable filename directly and
 "base-64" encode ones that aren't directly suitable.  The two versions of
 object filenames indicate the encoding:
 
+       =============== =============== ===============
        OBJECT TYPE     PRINTABLE       ENCODED
        =============== =============== ===============
        Index           "I..."          "J..."
        Data            "D..."          "E..."
        Special         "S..."          "T..."
+       =============== =============== ===============
 
 Intermediate directories are always "@" or "+" as appropriate.
 
@@ -307,8 +288,7 @@ Note that CacheFiles will erase from the cache any file it doesn't recognise or
 any file of an incorrect type (such as a FIFO file or a device file).
 
 
-==========================
-SECURITY MODEL AND SELINUX
+Security Model and SELinux
 ==========================
 
 CacheFiles is implemented to deal properly with the LSM security features of
@@ -331,26 +311,26 @@ When the CacheFiles module is asked to bind to its cache, it:
 
  (1) Finds the security label attached to the root cache directory and uses
      that as the security label with which it will create files.  By default,
-     this is:
+     this is::
 
        cachefiles_var_t
 
  (2) Finds the security label of the process which issued the bind request
-     (presumed to be the cachefilesd daemon), which by default will be:
+     (presumed to be the cachefilesd daemon), which by default will be::
 
        cachefilesd_t
 
      and asks LSM to supply a security ID as which it should act given the
-     daemon's label.  By default, this will be:
+     daemon's label.  By default, this will be::
 
        cachefiles_kernel_t
 
      SELinux transitions the daemon's security ID to the module's security ID
-     based on a rule of this form in the policy.
+     based on a rule of this form in the policy::
 
        type_transition <daemon's-ID> kernel_t : process <module's-ID>;
 
-     For instance:
+     For instance::
 
        type_transition cachefilesd_t kernel_t : process cachefiles_kernel_t;
 
@@ -370,7 +350,7 @@ There are policy source files available in:
 
        http://people.redhat.com/~dhowells/fscache/cachefilesd-0.8.tar.bz2
 
-and later versions.  In that tarball, see the files:
+and later versions.  In that tarball, see the files::
 
        cachefilesd.te
        cachefilesd.fc
@@ -379,7 +359,7 @@ and later versions.  In that tarball, see the files:
 They are built and installed directly by the RPM.
 
 If a non-RPM based system is being used, then copy the above files to their own
-directory and run:
+directory and run::
 
        make -f /usr/share/selinux/devel/Makefile
        semodule -i cachefilesd.pp
@@ -394,7 +374,7 @@ an auxiliary policy must be installed to label the alternate location of the
 cache.
 
 For instructions on how to add an auxiliary policy to enable the cache to be
-located elsewhere when SELinux is in enforcing mode, please see:
+located elsewhere when SELinux is in enforcing mode, please see::
 
        /usr/share/doc/cachefilesd-*/move-cache.txt
 
@@ -402,8 +382,7 @@ When the cachefilesd rpm is installed; alternatively, the document can be found
 in the sources.
 
 
-==================
-A NOTE ON SECURITY
+A Note on Security
 ==================
 
 CacheFiles makes use of the split security in the task_struct.  It allocates
@@ -445,17 +424,18 @@ for CacheFiles to run in a context of a specific security label, or to create
 files and directories with another security label.
 
 
-=======================
-STATISTICAL INFORMATION
+Statistical Information
 =======================
 
-If FS-Cache is compiled with the following option enabled:
+If FS-Cache is compiled with the following option enabled::
 
        CONFIG_CACHEFILES_HISTOGRAM=y
 
 then it will gather certain statistics and display them through a proc file.
 
- (*) /proc/fs/cachefiles/histogram
+ /proc/fs/cachefiles/histogram
+
+     ::
 
        cat /proc/fs/cachefiles/histogram
        JIFS  SECS  LOOKUPS   MKDIRS    CREATES
@@ -465,36 +445,39 @@ then it will gather certain statistics and display them through a proc file.
      between 0 jiffies and HZ-1 jiffies a variety of tasks took to run.  The
      columns are as follows:
 
+       =======         =======================================================
        COLUMN          TIME MEASUREMENT
        =======         =======================================================
        LOOKUPS         Length of time to perform a lookup on the backing fs
        MKDIRS          Length of time to perform a mkdir on the backing fs
        CREATES         Length of time to perform a create on the backing fs
+       =======         =======================================================
 
      Each row shows the number of events that took a particular range of times.
      Each step is 1 jiffy in size.  The JIFS column indicates the particular
      jiffy range covered, and the SECS field the equivalent number of seconds.
 
 
-=========
-DEBUGGING
+Debugging
 =========
 
 If CONFIG_CACHEFILES_DEBUG is enabled, the CacheFiles facility can have runtime
-debugging enabled by adjusting the value in:
+debugging enabled by adjusting the value in::
 
        /sys/module/cachefiles/parameters/debug
 
 This is a bitmask of debugging streams to enable:
 
+       ======= ======= =============================== =======================
        BIT     VALUE   STREAM                          POINT
        ======= ======= =============================== =======================
        0       1       General                         Function entry trace
        1       2                                       Function exit trace
        2       4                                       General
+       ======= ======= =============================== =======================
 
 The appropriate set of values should be OR'd together and the result written to
-the control file.  For example:
+the control file.  For example::
 
        echo $((1|4|8)) >/sys/module/cachefiles/parameters/debug
 
diff --git a/Documentation/filesystems/caching/fscache.rst b/Documentation/filesystems/caching/fscache.rst
new file mode 100644 (file)
index 0000000..70de869
--- /dev/null
@@ -0,0 +1,565 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+General Filesystem Caching
+==========================
+
+Overview
+========
+
+This facility is a general purpose cache for network filesystems, though it
+could be used for caching other things such as ISO9660 filesystems too.
+
+FS-Cache mediates between cache backends (such as CacheFS) and network
+filesystems::
+
+       +---------+
+       |         |                        +--------------+
+       |   NFS   |--+                     |              |
+       |         |  |                 +-->|   CacheFS    |
+       +---------+  |   +----------+  |   |  /dev/hda5   |
+                    |   |          |  |   +--------------+
+       +---------+  +-->|          |  |
+       |         |      |          |--+
+       |   AFS   |----->| FS-Cache |
+       |         |      |          |--+
+       +---------+  +-->|          |  |
+                    |   |          |  |   +--------------+
+       +---------+  |   +----------+  |   |              |
+       |         |  |                 +-->|  CacheFiles  |
+       |  ISOFS  |--+                     |  /var/cache  |
+       |         |                        +--------------+
+       +---------+
+
+Or to look at it another way, FS-Cache is a module that provides a caching
+facility to a network filesystem such that the cache is transparent to the
+user::
+
+       +---------+
+       |         |
+       | Server  |
+       |         |
+       +---------+
+            |                  NETWORK
+       ~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+            |
+            |           +----------+
+            V           |          |
+       +---------+      |          |
+       |         |      |          |
+       |   NFS   |----->| FS-Cache |
+       |         |      |          |--+
+       +---------+      |          |  |   +--------------+   +--------------+
+            |           |          |  |   |              |   |              |
+            V           +----------+  +-->|  CacheFiles  |-->|  Ext3        |
+       +---------+                        |  /var/cache  |   |  /dev/sda6   |
+       |         |                        +--------------+   +--------------+
+       |   VFS   |                                ^                     ^
+       |         |                                |                     |
+       +---------+                                +--------------+      |
+            |                  KERNEL SPACE                      |      |
+       ~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~|~~~~
+            |                  USER SPACE                        |      |
+            V                                                    |      |
+       +---------+                                           +--------------+
+       |         |                                           |              |
+       | Process |                                           | cachefilesd  |
+       |         |                                           |              |
+       +---------+                                           +--------------+
+
+
+FS-Cache does not follow the idea of completely loading every netfs file
+opened in its entirety into a cache before permitting it to be accessed and
+then serving the pages out of that cache rather than the netfs inode because:
+
+ (1) It must be practical to operate without a cache.
+
+ (2) The size of any accessible file must not be limited to the size of the
+     cache.
+
+ (3) The combined size of all opened files (this includes mapped libraries)
+     must not be limited to the size of the cache.
+
+ (4) The user should not be forced to download an entire file just to do a
+     one-off access of a small portion of it (such as might be done with the
+     "file" program).
+
+It instead serves the cache out in PAGE_SIZE chunks as and when requested by
+the netfs('s) using it.
+
+
+FS-Cache provides the following facilities:
+
+ (1) More than one cache can be used at once.  Caches can be selected
+     explicitly by use of tags.
+
+ (2) Caches can be added / removed at any time.
+
+ (3) The netfs is provided with an interface that allows either party to
+     withdraw caching facilities from a file (required for (2)).
+
+ (4) The interface to the netfs returns as few errors as possible, preferring
+     rather to let the netfs remain oblivious.
+
+ (5) Cookies are used to represent indices, files and other objects to the
+     netfs.  The simplest cookie is just a NULL pointer - indicating nothing
+     cached there.
+
+ (6) The netfs is allowed to propose - dynamically - any index hierarchy it
+     desires, though it must be aware that the index search function is
+     recursive, stack space is limited, and indices can only be children of
+     indices.
+
+ (7) Data I/O is done direct to and from the netfs's pages.  The netfs
+     indicates that page A is at index B of the data-file represented by cookie
+     C, and that it should be read or written.  The cache backend may or may
+     not start I/O on that page, but if it does, a netfs callback will be
+     invoked to indicate completion.  The I/O may be either synchronous or
+     asynchronous.
+
+ (8) Cookies can be "retired" upon release.  At this point FS-Cache will mark
+     them as obsolete and the index hierarchy rooted at that point will get
+     recycled.
+
+ (9) The netfs provides a "match" function for index searches.  In addition to
+     saying whether a match was made or not, this can also specify that an
+     entry should be updated or deleted.
+
+(10) As much as possible is done asynchronously.
+
+
+FS-Cache maintains a virtual indexing tree in which all indices, files, objects
+and pages are kept.  Bits of this tree may actually reside in one or more
+caches::
+
+                                            FSDEF
+                                              |
+                         +------------------------------------+
+                         |                                    |
+                        NFS                                  AFS
+                         |                                    |
+            +--------------------------+                +-----------+
+            |                          |                |           |
+         homedir                     mirror          afs.org   redhat.com
+            |                          |                            |
+      +------------+           +---------------+              +----------+
+      |            |           |               |              |          |
+    00001        00002       00007           00125        vol00001   vol00002
+      |            |           |               |                         |
+  +---+---+     +-----+      +---+      +------+------+            +-----+----+
+  |   |   |     |     |      |   |      |      |      |            |     |    |
+ PG0 PG1 PG2   PG0  XATTR   PG0 PG1   DIRENT DIRENT DIRENT        R/W   R/O  Bak
+                      |                                            |
+                     PG0                                       +-------+
+                                                               |       |
+                                                             00001   00003
+                                                               |
+                                                           +---+---+
+                                                           |   |   |
+                                                          PG0 PG1 PG2
+
+In the example above, you can see two netfs's being backed: NFS and AFS.  These
+have different index hierarchies:
+
+   * The NFS primary index contains per-server indices.  Each server index is
+     indexed by NFS file handles to get data file objects.  Each data file
+     objects can have an array of pages, but may also have further child
+     objects, such as extended attributes and directory entries.  Extended
+     attribute objects themselves have page-array contents.
+
+   * The AFS primary index contains per-cell indices.  Each cell index contains
+     per-logical-volume indices.  Each of volume index contains up to three
+     indices for the read-write, read-only and backup mirrors of those volumes.
+     Each of these contains vnode data file objects, each of which contains an
+     array of pages.
+
+The very top index is the FS-Cache master index in which individual netfs's
+have entries.
+
+Any index object may reside in more than one cache, provided it only has index
+children.  Any index with non-index object children will be assumed to only
+reside in one cache.
+
+
+The netfs API to FS-Cache can be found in:
+
+       Documentation/filesystems/caching/netfs-api.rst
+
+The cache backend API to FS-Cache can be found in:
+
+       Documentation/filesystems/caching/backend-api.rst
+
+A description of the internal representations and object state machine can be
+found in:
+
+       Documentation/filesystems/caching/object.rst
+
+
+Statistical Information
+=======================
+
+If FS-Cache is compiled with the following options enabled::
+
+       CONFIG_FSCACHE_STATS=y
+       CONFIG_FSCACHE_HISTOGRAM=y
+
+then it will gather certain statistics and display them through a number of
+proc files.
+
+/proc/fs/fscache/stats
+----------------------
+
+     This shows counts of a number of events that can happen in FS-Cache:
+
++--------------+-------+-------------------------------------------------------+
+|CLASS         |EVENT  |MEANING                                                |
++==============+=======+=======================================================+
+|Cookies       |idx=N  |Number of index cookies allocated                      |
++              +-------+-------------------------------------------------------+
+|              |dat=N  |Number of data storage cookies allocated               |
++              +-------+-------------------------------------------------------+
+|              |spc=N  |Number of special cookies allocated                    |
++--------------+-------+-------------------------------------------------------+
+|Objects       |alc=N  |Number of objects allocated                            |
++              +-------+-------------------------------------------------------+
+|              |nal=N  |Number of object allocation failures                   |
++              +-------+-------------------------------------------------------+
+|              |avl=N  |Number of objects that reached the available state     |
++              +-------+-------------------------------------------------------+
+|              |ded=N  |Number of objects that reached the dead state          |
++--------------+-------+-------------------------------------------------------+
+|ChkAux        |non=N  |Number of objects that didn't have a coherency check   |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of objects that passed a coherency check        |
++              +-------+-------------------------------------------------------+
+|              |upd=N  |Number of objects that needed a coherency data update  |
++              +-------+-------------------------------------------------------+
+|              |obs=N  |Number of objects that were declared obsolete          |
++--------------+-------+-------------------------------------------------------+
+|Pages         |mrk=N  |Number of pages marked as being cached                 |
+|              |unc=N  |Number of uncache page requests seen                   |
++--------------+-------+-------------------------------------------------------+
+|Acquire       |n=N    |Number of acquire cookie requests seen                 |
++              +-------+-------------------------------------------------------+
+|              |nul=N  |Number of acq reqs given a NULL parent                 |
++              +-------+-------------------------------------------------------+
+|              |noc=N  |Number of acq reqs rejected due to no cache available  |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of acq reqs succeeded                           |
++              +-------+-------------------------------------------------------+
+|              |nbf=N  |Number of acq reqs rejected due to error               |
++              +-------+-------------------------------------------------------+
+|              |oom=N  |Number of acq reqs failed on ENOMEM                    |
++--------------+-------+-------------------------------------------------------+
+|Lookups       |n=N    |Number of lookup calls made on cache backends          |
++              +-------+-------------------------------------------------------+
+|              |neg=N  |Number of negative lookups made                        |
++              +-------+-------------------------------------------------------+
+|              |pos=N  |Number of positive lookups made                        |
++              +-------+-------------------------------------------------------+
+|              |crt=N  |Number of objects created by lookup                    |
++              +-------+-------------------------------------------------------+
+|              |tmo=N  |Number of lookups timed out and requeued               |
++--------------+-------+-------------------------------------------------------+
+|Updates       |n=N    |Number of update cookie requests seen                  |
++              +-------+-------------------------------------------------------+
+|              |nul=N  |Number of upd reqs given a NULL parent                 |
++              +-------+-------------------------------------------------------+
+|              |run=N  |Number of upd reqs granted CPU time                    |
++--------------+-------+-------------------------------------------------------+
+|Relinqs       |n=N    |Number of relinquish cookie requests seen              |
++              +-------+-------------------------------------------------------+
+|              |nul=N  |Number of rlq reqs given a NULL parent                 |
++              +-------+-------------------------------------------------------+
+|              |wcr=N  |Number of rlq reqs waited on completion of creation    |
++--------------+-------+-------------------------------------------------------+
+|AttrChg       |n=N    |Number of attribute changed requests seen              |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of attr changed requests queued                 |
++              +-------+-------------------------------------------------------+
+|              |nbf=N  |Number of attr changed rejected -ENOBUFS               |
++              +-------+-------------------------------------------------------+
+|              |oom=N  |Number of attr changed failed -ENOMEM                  |
++              +-------+-------------------------------------------------------+
+|              |run=N  |Number of attr changed ops given CPU time              |
++--------------+-------+-------------------------------------------------------+
+|Allocs        |n=N    |Number of allocation requests seen                     |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of successful alloc reqs                        |
++              +-------+-------------------------------------------------------+
+|              |wt=N   |Number of alloc reqs that waited on lookup completion  |
++              +-------+-------------------------------------------------------+
+|              |nbf=N  |Number of alloc reqs rejected -ENOBUFS                 |
++              +-------+-------------------------------------------------------+
+|              |int=N  |Number of alloc reqs aborted -ERESTARTSYS              |
++              +-------+-------------------------------------------------------+
+|              |ops=N  |Number of alloc reqs submitted                         |
++              +-------+-------------------------------------------------------+
+|              |owt=N  |Number of alloc reqs waited for CPU time               |
++              +-------+-------------------------------------------------------+
+|              |abt=N  |Number of alloc reqs aborted due to object death       |
++--------------+-------+-------------------------------------------------------+
+|Retrvls       |n=N    |Number of retrieval (read) requests seen               |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of successful retr reqs                         |
++              +-------+-------------------------------------------------------+
+|              |wt=N   |Number of retr reqs that waited on lookup completion   |
++              +-------+-------------------------------------------------------+
+|              |nod=N  |Number of retr reqs returned -ENODATA                  |
++              +-------+-------------------------------------------------------+
+|              |nbf=N  |Number of retr reqs rejected -ENOBUFS                  |
++              +-------+-------------------------------------------------------+
+|              |int=N  |Number of retr reqs aborted -ERESTARTSYS               |
++              +-------+-------------------------------------------------------+
+|              |oom=N  |Number of retr reqs failed -ENOMEM                     |
++              +-------+-------------------------------------------------------+
+|              |ops=N  |Number of retr reqs submitted                          |
++              +-------+-------------------------------------------------------+
+|              |owt=N  |Number of retr reqs waited for CPU time                |
++              +-------+-------------------------------------------------------+
+|              |abt=N  |Number of retr reqs aborted due to object death        |
++--------------+-------+-------------------------------------------------------+
+|Stores        |n=N    |Number of storage (write) requests seen                |
++              +-------+-------------------------------------------------------+
+|              |ok=N   |Number of successful store reqs                        |
++              +-------+-------------------------------------------------------+
+|              |agn=N  |Number of store reqs on a page already pending storage |
++              +-------+-------------------------------------------------------+
+|              |nbf=N  |Number of store reqs rejected -ENOBUFS                 |
++              +-------+-------------------------------------------------------+
+|              |oom=N  |Number of store reqs failed -ENOMEM                    |
++              +-------+-------------------------------------------------------+
+|              |ops=N  |Number of store reqs submitted                         |
++              +-------+-------------------------------------------------------+
+|              |run=N  |Number of store reqs granted CPU time                  |
++              +-------+-------------------------------------------------------+
+|              |pgs=N  |Number of pages given store req processing time        |
++              +-------+-------------------------------------------------------+
+|              |rxd=N  |Number of store reqs deleted from tracking tree        |
++              +-------+-------------------------------------------------------+
+|              |olm=N  |Number of store reqs over store limit                  |
++--------------+-------+-------------------------------------------------------+
+|VmScan        |nos=N  |Number of release reqs against pages with no           |
+|              |       |pending store                                          |
++              +-------+-------------------------------------------------------+
+|              |gon=N  |Number of release reqs against pages stored by         |
+|              |       |time lock granted                                      |
++              +-------+-------------------------------------------------------+
+|              |bsy=N  |Number of release reqs ignored due to in-progress store|
++              +-------+-------------------------------------------------------+
+|              |can=N  |Number of page stores cancelled due to release req     |
++--------------+-------+-------------------------------------------------------+
+|Ops           |pend=N |Number of times async ops added to pending queues      |
++              +-------+-------------------------------------------------------+
+|              |run=N  |Number of times async ops given CPU time               |
++              +-------+-------------------------------------------------------+
+|              |enq=N  |Number of times async ops queued for processing        |
++              +-------+-------------------------------------------------------+
+|              |can=N  |Number of async ops cancelled                          |
++              +-------+-------------------------------------------------------+
+|              |rej=N  |Number of async ops rejected due to object             |
+|              |       |lookup/create failure                                  |
++              +-------+-------------------------------------------------------+
+|              |ini=N  |Number of async ops initialised                        |
++              +-------+-------------------------------------------------------+
+|              |dfr=N  |Number of async ops queued for deferred release        |
++              +-------+-------------------------------------------------------+
+|              |rel=N  |Number of async ops released                           |
+|              |       |(should equal ini=N when idle)                         |
++              +-------+-------------------------------------------------------+
+|              |gc=N   |Number of deferred-release async ops garbage collected |
++--------------+-------+-------------------------------------------------------+
+|CacheOp       |alo=N  |Number of in-progress alloc_object() cache ops         |
++              +-------+-------------------------------------------------------+
+|              |luo=N  |Number of in-progress lookup_object() cache ops        |
++              +-------+-------------------------------------------------------+
+|              |luc=N  |Number of in-progress lookup_complete() cache ops      |
++              +-------+-------------------------------------------------------+
+|              |gro=N  |Number of in-progress grab_object() cache ops          |
++              +-------+-------------------------------------------------------+
+|              |upo=N  |Number of in-progress update_object() cache ops        |
++              +-------+-------------------------------------------------------+
+|              |dro=N  |Number of in-progress drop_object() cache ops          |
++              +-------+-------------------------------------------------------+
+|              |pto=N  |Number of in-progress put_object() cache ops           |
++              +-------+-------------------------------------------------------+
+|              |syn=N  |Number of in-progress sync_cache() cache ops           |
++              +-------+-------------------------------------------------------+
+|              |atc=N  |Number of in-progress attr_changed() cache ops         |
++              +-------+-------------------------------------------------------+
+|              |rap=N  |Number of in-progress read_or_alloc_page() cache ops   |
++              +-------+-------------------------------------------------------+
+|              |ras=N  |Number of in-progress read_or_alloc_pages() cache ops  |
++              +-------+-------------------------------------------------------+
+|              |alp=N  |Number of in-progress allocate_page() cache ops        |
++              +-------+-------------------------------------------------------+
+|              |als=N  |Number of in-progress allocate_pages() cache ops       |
++              +-------+-------------------------------------------------------+
+|              |wrp=N  |Number of in-progress write_page() cache ops           |
++              +-------+-------------------------------------------------------+
+|              |ucp=N  |Number of in-progress uncache_page() cache ops         |
++              +-------+-------------------------------------------------------+
+|              |dsp=N  |Number of in-progress dissociate_pages() cache ops     |
++--------------+-------+-------------------------------------------------------+
+|CacheEv       |nsp=N  |Number of object lookups/creations rejected due to     |
+|              |       |lack of space                                          |
++              +-------+-------------------------------------------------------+
+|              |stl=N  |Number of stale objects deleted                        |
++              +-------+-------------------------------------------------------+
+|              |rtr=N  |Number of objects retired when relinquished            |
++              +-------+-------------------------------------------------------+
+|              |cul=N  |Number of objects culled                               |
++--------------+-------+-------------------------------------------------------+
+
+
+
+/proc/fs/fscache/histogram
+--------------------------
+
+     ::
+
+       cat /proc/fs/fscache/histogram
+       JIFS  SECS  OBJ INST  OP RUNS   OBJ RUNS  RETRV DLY RETRIEVLS
+       ===== ===== ========= ========= ========= ========= =========
+
+     This shows the breakdown of the number of times each amount of time
+     between 0 jiffies and HZ-1 jiffies a variety of tasks took to run.  The
+     columns are as follows:
+
+       =========       =======================================================
+       COLUMN          TIME MEASUREMENT
+       =========       =======================================================
+       OBJ INST        Length of time to instantiate an object
+       OP RUNS         Length of time a call to process an operation took
+       OBJ RUNS        Length of time a call to process an object event took
+       RETRV DLY       Time between an requesting a read and lookup completing
+       RETRIEVLS       Time between beginning and end of a retrieval
+       =========       =======================================================
+
+     Each row shows the number of events that took a particular range of times.
+     Each step is 1 jiffy in size.  The JIFS column indicates the particular
+     jiffy range covered, and the SECS field the equivalent number of seconds.
+
+
+
+Object List
+===========
+
+If CONFIG_FSCACHE_OBJECT_LIST is enabled, the FS-Cache facility will maintain a
+list of all the objects currently allocated and allow them to be viewed
+through::
+
+       /proc/fs/fscache/objects
+
+This will look something like::
+
+       [root@andromeda ~]# head /proc/fs/fscache/objects
+       OBJECT   PARENT   STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKIE_DEF TY FL NETFS_DATA       OBJECT_KEY, AUX_DATA
+       ======== ======== ==== ===== === === === == ===== == == = = | ================ == == ================ ================
+          17e4b        2 ACTV     0   0   0   0  0     0 7b  4 0 0 | NFS.fh           DT  0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a
+          1693a        2 ACTV     0   0   0   0  0     0 7b  4 0 0 | NFS.fh           DT  0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a
+
+where the first set of columns before the '|' describe the object:
+
+       ======= ===============================================================
+       COLUMN  DESCRIPTION
+       ======= ===============================================================
+       OBJECT  Object debugging ID (appears as OBJ%x in some debug messages)
+       PARENT  Debugging ID of parent object
+       STAT    Object state
+       CHLDN   Number of child objects of this object
+       OPS     Number of outstanding operations on this object
+       OOP     Number of outstanding child object management operations
+       IPR
+       EX      Number of outstanding exclusive operations
+       READS   Number of outstanding read operations
+       EM      Object's event mask
+       EV      Events raised on this object
+       F       Object flags
+       S       Object work item busy state mask (1:pending 2:running)
+       ======= ===============================================================
+
+and the second set of columns describe the object's cookie, if present:
+
+       ================ ======================================================
+       COLUMN           DESCRIPTION
+       ================ ======================================================
+       NETFS_COOKIE_DEF Name of netfs cookie definition
+       TY               Cookie type (IX - index, DT - data, hex - special)
+       FL               Cookie flags
+       NETFS_DATA       Netfs private data stored in the cookie
+       OBJECT_KEY       Object key } 1 column, with separating comma
+       AUX_DATA         Object aux data } presence may be configured
+       ================ ======================================================
+
+The data shown may be filtered by attaching the a key to an appropriate keyring
+before viewing the file.  Something like::
+
+               keyctl add user fscache:objlist <restrictions> @s
+
+where <restrictions> are a selection of the following letters:
+
+       ==      =========================================================
+       K       Show hexdump of object key (don't show if not given)
+       A       Show hexdump of object aux data (don't show if not given)
+       ==      =========================================================
+
+and the following paired letters:
+
+       ==      =========================================================
+       C       Show objects that have a cookie
+       c       Show objects that don't have a cookie
+       B       Show objects that are busy
+       b       Show objects that aren't busy
+       W       Show objects that have pending writes
+       w       Show objects that don't have pending writes
+       R       Show objects that have outstanding reads
+       r       Show objects that don't have outstanding reads
+       S       Show objects that have work queued
+       s       Show objects that don't have work queued
+       ==      =========================================================
+
+If neither side of a letter pair is given, then both are implied.  For example:
+
+       keyctl add user fscache:objlist KB @s
+
+shows objects that are busy, and lists their object keys, but does not dump
+their auxiliary data.  It also implies "CcWwRrSs", but as 'B' is given, 'b' is
+not implied.
+
+By default all objects and all fields will be shown.
+
+
+Debugging
+=========
+
+If CONFIG_FSCACHE_DEBUG is enabled, the FS-Cache facility can have runtime
+debugging enabled by adjusting the value in::
+
+       /sys/module/fscache/parameters/debug
+
+This is a bitmask of debugging streams to enable:
+
+       ======= ======= =============================== =======================
+       BIT     VALUE   STREAM                          POINT
+       ======= ======= =============================== =======================
+       0       1       Cache management                Function entry trace
+       1       2                                       Function exit trace
+       2       4                                       General
+       3       8       Cookie management               Function entry trace
+       4       16                                      Function exit trace
+       5       32                                      General
+       6       64      Page handling                   Function entry trace
+       7       128                                     Function exit trace
+       8       256                                     General
+       9       512     Operation management            Function entry trace
+       10      1024                                    Function exit trace
+       11      2048                                    General
+       ======= ======= =============================== =======================
+
+The appropriate set of values should be OR'd together and the result written to
+the control file.  For example::
+
+       echo $((1|8|64)) >/sys/module/fscache/parameters/debug
+
+will turn on all function entry debugging.
diff --git a/Documentation/filesystems/caching/fscache.txt b/Documentation/filesystems/caching/fscache.txt
deleted file mode 100644 (file)
index 50f0a57..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-                         ==========================
-                         General Filesystem Caching
-                         ==========================
-
-========
-OVERVIEW
-========
-
-This facility is a general purpose cache for network filesystems, though it
-could be used for caching other things such as ISO9660 filesystems too.
-
-FS-Cache mediates between cache backends (such as CacheFS) and network
-filesystems:
-
-       +---------+
-       |         |                        +--------------+
-       |   NFS   |--+                     |              |
-       |         |  |                 +-->|   CacheFS    |
-       +---------+  |   +----------+  |   |  /dev/hda5   |
-                    |   |          |  |   +--------------+
-       +---------+  +-->|          |  |
-       |         |      |          |--+
-       |   AFS   |----->| FS-Cache |
-       |         |      |          |--+
-       +---------+  +-->|          |  |
-                    |   |          |  |   +--------------+
-       +---------+  |   +----------+  |   |              |
-       |         |  |                 +-->|  CacheFiles  |
-       |  ISOFS  |--+                     |  /var/cache  |
-       |         |                        +--------------+
-       +---------+
-
-Or to look at it another way, FS-Cache is a module that provides a caching
-facility to a network filesystem such that the cache is transparent to the
-user:
-
-       +---------+
-       |         |
-       | Server  |
-       |         |
-       +---------+
-            |                  NETWORK
-       ~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-            |
-            |           +----------+
-            V           |          |
-       +---------+      |          |
-       |         |      |          |
-       |   NFS   |----->| FS-Cache |
-       |         |      |          |--+
-       +---------+      |          |  |   +--------------+   +--------------+
-            |           |          |  |   |              |   |              |
-            V           +----------+  +-->|  CacheFiles  |-->|  Ext3        |
-       +---------+                        |  /var/cache  |   |  /dev/sda6   |
-       |         |                        +--------------+   +--------------+
-       |   VFS   |                                ^                     ^
-       |         |                                |                     |
-       +---------+                                +--------------+      |
-            |                  KERNEL SPACE                      |      |
-       ~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~|~~~~
-            |                  USER SPACE                        |      |
-            V                                                    |      |
-       +---------+                                           +--------------+
-       |         |                                           |              |
-       | Process |                                           | cachefilesd  |
-       |         |                                           |              |
-       +---------+                                           +--------------+
-
-
-FS-Cache does not follow the idea of completely loading every netfs file
-opened in its entirety into a cache before permitting it to be accessed and
-then serving the pages out of that cache rather than the netfs inode because:
-
- (1) It must be practical to operate without a cache.
-
- (2) The size of any accessible file must not be limited to the size of the
-     cache.
-
- (3) The combined size of all opened files (this includes mapped libraries)
-     must not be limited to the size of the cache.
-
- (4) The user should not be forced to download an entire file just to do a
-     one-off access of a small portion of it (such as might be done with the
-     "file" program).
-
-It instead serves the cache out in PAGE_SIZE chunks as and when requested by
-the netfs('s) using it.
-
-
-FS-Cache provides the following facilities:
-
- (1) More than one cache can be used at once.  Caches can be selected
-     explicitly by use of tags.
-
- (2) Caches can be added / removed at any time.
-
- (3) The netfs is provided with an interface that allows either party to
-     withdraw caching facilities from a file (required for (2)).
-
- (4) The interface to the netfs returns as few errors as possible, preferring
-     rather to let the netfs remain oblivious.
-
- (5) Cookies are used to represent indices, files and other objects to the
-     netfs.  The simplest cookie is just a NULL pointer - indicating nothing
-     cached there.
-
- (6) The netfs is allowed to propose - dynamically - any index hierarchy it
-     desires, though it must be aware that the index search function is
-     recursive, stack space is limited, and indices can only be children of
-     indices.
-
- (7) Data I/O is done direct to and from the netfs's pages.  The netfs
-     indicates that page A is at index B of the data-file represented by cookie
-     C, and that it should be read or written.  The cache backend may or may
-     not start I/O on that page, but if it does, a netfs callback will be
-     invoked to indicate completion.  The I/O may be either synchronous or
-     asynchronous.
-
- (8) Cookies can be "retired" upon release.  At this point FS-Cache will mark
-     them as obsolete and the index hierarchy rooted at that point will get
-     recycled.
-
- (9) The netfs provides a "match" function for index searches.  In addition to
-     saying whether a match was made or not, this can also specify that an
-     entry should be updated or deleted.
-
-(10) As much as possible is done asynchronously.
-
-
-FS-Cache maintains a virtual indexing tree in which all indices, files, objects
-and pages are kept.  Bits of this tree may actually reside in one or more
-caches.
-
-                                           FSDEF
-                                             |
-                        +------------------------------------+
-                        |                                    |
-                       NFS                                  AFS
-                        |                                    |
-           +--------------------------+                +-----------+
-           |                          |                |           |
-        homedir                     mirror          afs.org   redhat.com
-           |                          |                            |
-     +------------+           +---------------+              +----------+
-     |            |           |               |              |          |
-   00001        00002       00007           00125        vol00001   vol00002
-     |            |           |               |                         |
- +---+---+     +-----+      +---+      +------+------+            +-----+----+
- |   |   |     |     |      |   |      |      |      |            |     |    |
-PG0 PG1 PG2   PG0  XATTR   PG0 PG1   DIRENT DIRENT DIRENT        R/W   R/O  Bak
-                     |                                            |
-                    PG0                                       +-------+
-                                                              |       |
-                                                            00001   00003
-                                                              |
-                                                          +---+---+
-                                                          |   |   |
-                                                         PG0 PG1 PG2
-
-In the example above, you can see two netfs's being backed: NFS and AFS.  These
-have different index hierarchies:
-
- (*) The NFS primary index contains per-server indices.  Each server index is
-     indexed by NFS file handles to get data file objects.  Each data file
-     objects can have an array of pages, but may also have further child
-     objects, such as extended attributes and directory entries.  Extended
-     attribute objects themselves have page-array contents.
-
- (*) The AFS primary index contains per-cell indices.  Each cell index contains
-     per-logical-volume indices.  Each of volume index contains up to three
-     indices for the read-write, read-only and backup mirrors of those volumes.
-     Each of these contains vnode data file objects, each of which contains an
-     array of pages.
-
-The very top index is the FS-Cache master index in which individual netfs's
-have entries.
-
-Any index object may reside in more than one cache, provided it only has index
-children.  Any index with non-index object children will be assumed to only
-reside in one cache.
-
-
-The netfs API to FS-Cache can be found in:
-
-       Documentation/filesystems/caching/netfs-api.txt
-
-The cache backend API to FS-Cache can be found in:
-
-       Documentation/filesystems/caching/backend-api.txt
-
-A description of the internal representations and object state machine can be
-found in:
-
-       Documentation/filesystems/caching/object.txt
-
-
-=======================
-STATISTICAL INFORMATION
-=======================
-
-If FS-Cache is compiled with the following options enabled:
-
-       CONFIG_FSCACHE_STATS=y
-       CONFIG_FSCACHE_HISTOGRAM=y
-
-then it will gather certain statistics and display them through a number of
-proc files.
-
- (*) /proc/fs/fscache/stats
-
-     This shows counts of a number of events that can happen in FS-Cache:
-
-       CLASS   EVENT   MEANING
-       ======= ======= =======================================================
-       Cookies idx=N   Number of index cookies allocated
-               dat=N   Number of data storage cookies allocated
-               spc=N   Number of special cookies allocated
-       Objects alc=N   Number of objects allocated
-               nal=N   Number of object allocation failures
-               avl=N   Number of objects that reached the available state
-               ded=N   Number of objects that reached the dead state
-       ChkAux  non=N   Number of objects that didn't have a coherency check
-               ok=N    Number of objects that passed a coherency check
-               upd=N   Number of objects that needed a coherency data update
-               obs=N   Number of objects that were declared obsolete
-       Pages   mrk=N   Number of pages marked as being cached
-               unc=N   Number of uncache page requests seen
-       Acquire n=N     Number of acquire cookie requests seen
-               nul=N   Number of acq reqs given a NULL parent
-               noc=N   Number of acq reqs rejected due to no cache available
-               ok=N    Number of acq reqs succeeded
-               nbf=N   Number of acq reqs rejected due to error
-               oom=N   Number of acq reqs failed on ENOMEM
-       Lookups n=N     Number of lookup calls made on cache backends
-               neg=N   Number of negative lookups made
-               pos=N   Number of positive lookups made
-               crt=N   Number of objects created by lookup
-               tmo=N   Number of lookups timed out and requeued
-       Updates n=N     Number of update cookie requests seen
-               nul=N   Number of upd reqs given a NULL parent
-               run=N   Number of upd reqs granted CPU time
-       Relinqs n=N     Number of relinquish cookie requests seen
-               nul=N   Number of rlq reqs given a NULL parent
-               wcr=N   Number of rlq reqs waited on completion of creation
-       AttrChg n=N     Number of attribute changed requests seen
-               ok=N    Number of attr changed requests queued
-               nbf=N   Number of attr changed rejected -ENOBUFS
-               oom=N   Number of attr changed failed -ENOMEM
-               run=N   Number of attr changed ops given CPU time
-       Allocs  n=N     Number of allocation requests seen
-               ok=N    Number of successful alloc reqs
-               wt=N    Number of alloc reqs that waited on lookup completion
-               nbf=N   Number of alloc reqs rejected -ENOBUFS
-               int=N   Number of alloc reqs aborted -ERESTARTSYS
-               ops=N   Number of alloc reqs submitted
-               owt=N   Number of alloc reqs waited for CPU time
-               abt=N   Number of alloc reqs aborted due to object death
-       Retrvls n=N     Number of retrieval (read) requests seen
-               ok=N    Number of successful retr reqs
-               wt=N    Number of retr reqs that waited on lookup completion
-               nod=N   Number of retr reqs returned -ENODATA
-               nbf=N   Number of retr reqs rejected -ENOBUFS
-               int=N   Number of retr reqs aborted -ERESTARTSYS
-               oom=N   Number of retr reqs failed -ENOMEM
-               ops=N   Number of retr reqs submitted
-               owt=N   Number of retr reqs waited for CPU time
-               abt=N   Number of retr reqs aborted due to object death
-       Stores  n=N     Number of storage (write) requests seen
-               ok=N    Number of successful store reqs
-               agn=N   Number of store reqs on a page already pending storage
-               nbf=N   Number of store reqs rejected -ENOBUFS
-               oom=N   Number of store reqs failed -ENOMEM
-               ops=N   Number of store reqs submitted
-               run=N   Number of store reqs granted CPU time
-               pgs=N   Number of pages given store req processing time
-               rxd=N   Number of store reqs deleted from tracking tree
-               olm=N   Number of store reqs over store limit
-       VmScan  nos=N   Number of release reqs against pages with no pending store
-               gon=N   Number of release reqs against pages stored by time lock granted
-               bsy=N   Number of release reqs ignored due to in-progress store
-               can=N   Number of page stores cancelled due to release req
-       Ops     pend=N  Number of times async ops added to pending queues
-               run=N   Number of times async ops given CPU time
-               enq=N   Number of times async ops queued for processing
-               can=N   Number of async ops cancelled
-               rej=N   Number of async ops rejected due to object lookup/create failure
-               ini=N   Number of async ops initialised
-               dfr=N   Number of async ops queued for deferred release
-               rel=N   Number of async ops released (should equal ini=N when idle)
-               gc=N    Number of deferred-release async ops garbage collected
-       CacheOp alo=N   Number of in-progress alloc_object() cache ops
-               luo=N   Number of in-progress lookup_object() cache ops
-               luc=N   Number of in-progress lookup_complete() cache ops
-               gro=N   Number of in-progress grab_object() cache ops
-               upo=N   Number of in-progress update_object() cache ops
-               dro=N   Number of in-progress drop_object() cache ops
-               pto=N   Number of in-progress put_object() cache ops
-               syn=N   Number of in-progress sync_cache() cache ops
-               atc=N   Number of in-progress attr_changed() cache ops
-               rap=N   Number of in-progress read_or_alloc_page() cache ops
-               ras=N   Number of in-progress read_or_alloc_pages() cache ops
-               alp=N   Number of in-progress allocate_page() cache ops
-               als=N   Number of in-progress allocate_pages() cache ops
-               wrp=N   Number of in-progress write_page() cache ops
-               ucp=N   Number of in-progress uncache_page() cache ops
-               dsp=N   Number of in-progress dissociate_pages() cache ops
-       CacheEv nsp=N   Number of object lookups/creations rejected due to lack of space
-               stl=N   Number of stale objects deleted
-               rtr=N   Number of objects retired when relinquished
-               cul=N   Number of objects culled
-
-
- (*) /proc/fs/fscache/histogram
-
-       cat /proc/fs/fscache/histogram
-       JIFS  SECS  OBJ INST  OP RUNS   OBJ RUNS  RETRV DLY RETRIEVLS
-       ===== ===== ========= ========= ========= ========= =========
-
-     This shows the breakdown of the number of times each amount of time
-     between 0 jiffies and HZ-1 jiffies a variety of tasks took to run.  The
-     columns are as follows:
-
-       COLUMN          TIME MEASUREMENT
-       =======         =======================================================
-       OBJ INST        Length of time to instantiate an object
-       OP RUNS         Length of time a call to process an operation took
-       OBJ RUNS        Length of time a call to process an object event took
-       RETRV DLY       Time between an requesting a read and lookup completing
-       RETRIEVLS       Time between beginning and end of a retrieval
-
-     Each row shows the number of events that took a particular range of times.
-     Each step is 1 jiffy in size.  The JIFS column indicates the particular
-     jiffy range covered, and the SECS field the equivalent number of seconds.
-
-
-===========
-OBJECT LIST
-===========
-
-If CONFIG_FSCACHE_OBJECT_LIST is enabled, the FS-Cache facility will maintain a
-list of all the objects currently allocated and allow them to be viewed
-through:
-
-       /proc/fs/fscache/objects
-
-This will look something like:
-
-       [root@andromeda ~]# head /proc/fs/fscache/objects
-       OBJECT   PARENT   STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKIE_DEF TY FL NETFS_DATA       OBJECT_KEY, AUX_DATA
-       ======== ======== ==== ===== === === === == ===== == == = = | ================ == == ================ ================
-          17e4b        2 ACTV     0   0   0   0  0     0 7b  4 0 0 | NFS.fh           DT  0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a
-          1693a        2 ACTV     0   0   0   0  0     0 7b  4 0 0 | NFS.fh           DT  0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a
-
-where the first set of columns before the '|' describe the object:
-
-       COLUMN  DESCRIPTION
-       ======= ===============================================================
-       OBJECT  Object debugging ID (appears as OBJ%x in some debug messages)
-       PARENT  Debugging ID of parent object
-       STAT    Object state
-       CHLDN   Number of child objects of this object
-       OPS     Number of outstanding operations on this object
-       OOP     Number of outstanding child object management operations
-       IPR
-       EX      Number of outstanding exclusive operations
-       READS   Number of outstanding read operations
-       EM      Object's event mask
-       EV      Events raised on this object
-       F       Object flags
-       S       Object work item busy state mask (1:pending 2:running)
-
-and the second set of columns describe the object's cookie, if present:
-
-       COLUMN          DESCRIPTION
-       =============== =======================================================
-       NETFS_COOKIE_DEF Name of netfs cookie definition
-       TY              Cookie type (IX - index, DT - data, hex - special)
-       FL              Cookie flags
-       NETFS_DATA      Netfs private data stored in the cookie
-       OBJECT_KEY      Object key      } 1 column, with separating comma
-       AUX_DATA        Object aux data } presence may be configured
-
-The data shown may be filtered by attaching the a key to an appropriate keyring
-before viewing the file.  Something like:
-
-               keyctl add user fscache:objlist <restrictions> @s
-
-where <restrictions> are a selection of the following letters:
-
-       K       Show hexdump of object key (don't show if not given)
-       A       Show hexdump of object aux data (don't show if not given)
-
-and the following paired letters:
-
-       C       Show objects that have a cookie
-       c       Show objects that don't have a cookie
-       B       Show objects that are busy
-       b       Show objects that aren't busy
-       W       Show objects that have pending writes
-       w       Show objects that don't have pending writes
-       R       Show objects that have outstanding reads
-       r       Show objects that don't have outstanding reads
-       S       Show objects that have work queued
-       s       Show objects that don't have work queued
-
-If neither side of a letter pair is given, then both are implied.  For example:
-
-       keyctl add user fscache:objlist KB @s
-
-shows objects that are busy, and lists their object keys, but does not dump
-their auxiliary data.  It also implies "CcWwRrSs", but as 'B' is given, 'b' is
-not implied.
-
-By default all objects and all fields will be shown.
-
-
-=========
-DEBUGGING
-=========
-
-If CONFIG_FSCACHE_DEBUG is enabled, the FS-Cache facility can have runtime
-debugging enabled by adjusting the value in:
-
-       /sys/module/fscache/parameters/debug
-
-This is a bitmask of debugging streams to enable:
-
-       BIT     VALUE   STREAM                          POINT
-       ======= ======= =============================== =======================
-       0       1       Cache management                Function entry trace
-       1       2                                       Function exit trace
-       2       4                                       General
-       3       8       Cookie management               Function entry trace
-       4       16                                      Function exit trace
-       5       32                                      General
-       6       64      Page handling                   Function entry trace
-       7       128                                     Function exit trace
-       8       256                                     General
-       9       512     Operation management            Function entry trace
-       10      1024                                    Function exit trace
-       11      2048                                    General
-
-The appropriate set of values should be OR'd together and the result written to
-the control file.  For example:
-
-       echo $((1|8|64)) >/sys/module/fscache/parameters/debug
-
-will turn on all function entry debugging.
diff --git a/Documentation/filesystems/caching/index.rst b/Documentation/filesystems/caching/index.rst
new file mode 100644 (file)
index 0000000..033da7a
--- /dev/null
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Filesystem Caching
+==================
+
+.. toctree::
+   :maxdepth: 2
+
+   fscache
+   object
+   backend-api
+   cachefiles
+   netfs-api
+   operations
similarity index 91%
rename from Documentation/filesystems/caching/netfs-api.txt
rename to Documentation/filesystems/caching/netfs-api.rst
index ba968e8f5704d1162c42223437e5b935e96404b4..d9f14b8610badaef96b552bb6a2d850feef07641 100644 (file)
@@ -1,6 +1,8 @@
-                       ===============================
-                       FS-CACHE NETWORK FILESYSTEM API
-                       ===============================
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+FS-Cache Network Filesystem API
+===============================
 
 There's an API by which a network filesystem can make use of the FS-Cache
 facilities.  This is based around a number of principles:
@@ -19,7 +21,7 @@ facilities.  This is based around a number of principles:
 
 This API is declared in <linux/fscache.h>.
 
-This document contains the following sections:
+.. This document contains the following sections:
 
         (1) Network filesystem definition
         (2) Index definition
@@ -41,12 +43,11 @@ This document contains the following sections:
        (18) FS-Cache specific page flags.
 
 
-=============================
-NETWORK FILESYSTEM DEFINITION
+Network Filesystem Definition
 =============================
 
 FS-Cache needs a description of the network filesystem.  This is specified
-using a record of the following structure:
+using a record of the following structure::
 
        struct fscache_netfs {
                uint32_t                        version;
@@ -71,7 +72,7 @@ The fields are:
      another parameter passed into the registration function.
 
 For example, kAFS (linux/fs/afs/) uses the following definitions to describe
-itself:
+itself::
 
        struct fscache_netfs afs_cache_netfs = {
                .version        = 0,
@@ -79,8 +80,7 @@ itself:
        };
 
 
-================
-INDEX DEFINITION
+Index Definition
 ================
 
 Indices are used for two purposes:
@@ -114,11 +114,10 @@ There are some limits on indices:
      function is recursive.  Too many layers will run the kernel out of stack.
 
 
-=================
-OBJECT DEFINITION
+Object Definition
 =================
 
-To define an object, a structure of the following type should be filled out:
+To define an object, a structure of the following type should be filled out::
 
        struct fscache_cookie_def
        {
@@ -149,16 +148,13 @@ This has the following fields:
 
      This is one of the following values:
 
-       (*) FSCACHE_COOKIE_TYPE_INDEX
-
+       FSCACHE_COOKIE_TYPE_INDEX
            This defines an index, which is a special FS-Cache type.
 
-       (*) FSCACHE_COOKIE_TYPE_DATAFILE
-
+       FSCACHE_COOKIE_TYPE_DATAFILE
            This defines an ordinary data file.
 
-       (*) Any other value between 2 and 255
-
+       Any other value between 2 and 255
            This defines an extraordinary object such as an XATTR.
 
  (2) The name of the object type (NUL terminated unless all 16 chars are used)
@@ -192,9 +188,14 @@ This has the following fields:
 
      If present, the function should return one of the following values:
 
-       (*) FSCACHE_CHECKAUX_OKAY               - the entry is okay as is
-       (*) FSCACHE_CHECKAUX_NEEDS_UPDATE       - the entry requires update
-       (*) FSCACHE_CHECKAUX_OBSOLETE           - the entry should be deleted
+       FSCACHE_CHECKAUX_OKAY
+           - the entry is okay as is
+
+       FSCACHE_CHECKAUX_NEEDS_UPDATE
+           - the entry requires update
+
+       FSCACHE_CHECKAUX_OBSOLETE
+           - the entry should be deleted
 
      This function can also be used to extract data from the auxiliary data in
      the cache and copy it into the netfs's structures.
@@ -236,32 +237,30 @@ This has the following fields:
      This function is not required for indices as they're not permitted data.
 
 
-===================================
-NETWORK FILESYSTEM (UN)REGISTRATION
+Network Filesystem (Un)registration
 ===================================
 
 The first step is to declare the network filesystem to the cache.  This also
 involves specifying the layout of the primary index (for AFS, this would be the
 "cell" level).
 
-The registration function is:
+The registration function is::
 
        int fscache_register_netfs(struct fscache_netfs *netfs);
 
 It just takes a pointer to the netfs definition.  It returns 0 or an error as
 appropriate.
 
-For kAFS, registration is done as follows:
+For kAFS, registration is done as follows::
 
        ret = fscache_register_netfs(&afs_cache_netfs);
 
-The last step is, of course, unregistration:
+The last step is, of course, unregistration::
 
        void fscache_unregister_netfs(struct fscache_netfs *netfs);
 
 
-================
-CACHE TAG LOOKUP
+Cache Tag Lookup
 ================
 
 FS-Cache permits the use of more than one cache.  To permit particular index
@@ -270,7 +269,7 @@ representation tags.  This step is optional; it can be left entirely up to
 FS-Cache as to which cache should be used.  The problem with doing that is that
 FS-Cache will always pick the first cache that was registered.
 
-To get the representation for a named tag:
+To get the representation for a named tag::
 
        struct fscache_cache_tag *fscache_lookup_cache_tag(const char *name);
 
@@ -278,7 +277,7 @@ This takes a text string as the name and returns a representation of a tag.  It
 will never return an error.  It may return a dummy tag, however, if it runs out
 of memory; this will inhibit caching with this tag.
 
-Any representation so obtained must be released by passing it to this function:
+Any representation so obtained must be released by passing it to this function::
 
        void fscache_release_cache_tag(struct fscache_cache_tag *tag);
 
@@ -286,13 +285,12 @@ The tag will be retrieved by FS-Cache when it calls the object definition
 operation select_cache().
 
 
-==================
-INDEX REGISTRATION
+Index Registration
 ==================
 
 The third step is to inform FS-Cache about part of an index hierarchy that can
 be used to locate files.  This is done by requesting a cookie for each index in
-the path to the file:
+the path to the file::
 
        struct fscache_cookie *
        fscache_acquire_cookie(struct fscache_cookie *parent,
@@ -339,7 +337,7 @@ must be enabled to do anything with it.  A disabled cookie can be enabled by
 calling fscache_enable_cookie() (see below).
 
 For example, with AFS, a cell would be added to the primary index.  This index
-entry would have a dependent inode containing volume mappings within this cell:
+entry would have a dependent inode containing volume mappings within this cell::
 
        cell->cache =
                fscache_acquire_cookie(afs_cache_netfs.primary_index,
@@ -349,7 +347,7 @@ entry would have a dependent inode containing volume mappings within this cell:
                                       cell, 0, true);
 
 And then a particular volume could be added to that index by ID, creating
-another index for vnodes (AFS inode equivalents):
+another index for vnodes (AFS inode equivalents)::
 
        volume->cache =
                fscache_acquire_cookie(volume->cell->cache,
@@ -359,13 +357,12 @@ another index for vnodes (AFS inode equivalents):
                                       volume, 0, true);
 
 
-======================
-DATA FILE REGISTRATION
+Data File Registration
 ======================
 
 The fourth step is to request a data file be created in the cache.  This is
 identical to index cookie acquisition.  The only difference is that the type in
-the object definition should be something other than index type.
+the object definition should be something other than index type::
 
        vnode->cache =
                fscache_acquire_cookie(volume->cache,
@@ -375,15 +372,14 @@ the object definition should be something other than index type.
                                       vnode, vnode->status.size, true);
 
 
-=================================
-MISCELLANEOUS OBJECT REGISTRATION
+Miscellaneous Object Registration
 =================================
 
 An optional step is to request an object of miscellaneous type be created in
 the cache.  This is almost identical to index cookie acquisition.  The only
 difference is that the type in the object definition should be something other
 than index type.  While the parent object could be an index, it's more likely
-it would be some other type of object such as a data file.
+it would be some other type of object such as a data file::
 
        xattr->cache =
                fscache_acquire_cookie(vnode->cache,
@@ -396,13 +392,12 @@ Miscellaneous objects might be used to store extended attributes or directory
 entries for example.
 
 
-==========================
-SETTING THE DATA FILE SIZE
+Setting the Data File Size
 ==========================
 
 The fifth step is to set the physical attributes of the file, such as its size.
 This doesn't automatically reserve any space in the cache, but permits the
-cache to adjust its metadata for data tracking appropriately:
+cache to adjust its metadata for data tracking appropriately::
 
        int fscache_attr_changed(struct fscache_cookie *cookie);
 
@@ -417,8 +412,7 @@ some point in the future, and as such, it may happen after the function returns
 to the caller.  The attribute adjustment excludes read and write operations.
 
 
-=====================
-PAGE ALLOC/READ/WRITE
+Page alloc/read/write
 =====================
 
 And the sixth step is to store and retrieve pages in the cache.  There are
@@ -441,7 +435,7 @@ PAGE READ
 
 Firstly, the netfs should ask FS-Cache to examine the caches and read the
 contents cached for a particular page of a particular file if present, or else
-allocate space to store the contents if not:
+allocate space to store the contents if not::
 
        typedef
        void (*fscache_rw_complete_t)(struct page *page,
@@ -474,14 +468,14 @@ Else if there's a copy of the page resident in the cache:
 
  (4) When the read is complete, end_io_func() will be invoked with:
 
-     (*) The netfs data supplied when the cookie was created.
+       * The netfs data supplied when the cookie was created.
 
-     (*) The page descriptor.
+       * The page descriptor.
 
-     (*) The context argument passed to the above function.  This will be
+       * The context argument passed to the above function.  This will be
          maintained with the get_context/put_context functions mentioned above.
 
-     (*) An argument that's 0 on success or negative for an error code.
+       * An argument that's 0 on success or negative for an error code.
 
      If an error occurs, it should be assumed that the page contains no usable
      data.  fscache_readpages_cancel() may need to be called.
@@ -504,11 +498,11 @@ This function may also return -ENOMEM or -EINTR, in which case it won't have
 read any data from the cache.
 
 
-PAGE ALLOCATE
+Page Allocate
 -------------
 
 Alternatively, if there's not expected to be any data in the cache for a page
-because the file has been extended, a block can simply be allocated instead:
+because the file has been extended, a block can simply be allocated instead::
 
        int fscache_alloc_page(struct fscache_cookie *cookie,
                               struct page *page,
@@ -523,12 +517,12 @@ The mark_pages_cached() cookie operation will be called on the page if
 successful.
 
 
-PAGE WRITE
+Page Write
 ----------
 
 Secondly, if the netfs changes the contents of the page (either due to an
 initial download or if a user performs a write), then the page should be
-written back to the cache:
+written back to the cache::
 
        int fscache_write_page(struct fscache_cookie *cookie,
                               struct page *page,
@@ -566,11 +560,11 @@ place if unforeseen circumstances arose (such as a disk error).
 Writing takes place asynchronously.
 
 
-MULTIPLE PAGE READ
+Multiple Page Read
 ------------------
 
 A facility is provided to read several pages at once, as requested by the
-readpages() address space operation:
+readpages() address space operation::
 
        int fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
                                        struct address_space *mapping,
@@ -598,7 +592,7 @@ This works in a similar way to fscache_read_or_alloc_page(), except:
      be returned.
 
      Otherwise, if all pages had reads dispatched, then 0 will be returned, the
-     list will be empty and *nr_pages will be 0.
+     list will be empty and ``*nr_pages`` will be 0.
 
  (4) end_io_func will be called once for each page being read as the reads
      complete.  It will be called in process context if error != 0, but it may
@@ -609,13 +603,13 @@ some of the pages being read and some being allocated.  Those pages will have
 been marked appropriately and will need uncaching.
 
 
-CANCELLATION OF UNREAD PAGES
+Cancellation of Unread Pages
 ----------------------------
 
 If one or more pages are passed to fscache_read_or_alloc_pages() but not then
 read from the cache and also not read from the underlying filesystem then
 those pages will need to have any marks and reservations removed.  This can be
-done by calling:
+done by calling::
 
        void fscache_readpages_cancel(struct fscache_cookie *cookie,
                                      struct list_head *pages);
@@ -625,11 +619,10 @@ fscache_read_or_alloc_pages().  Every page in the pages list will be examined
 and any that have PG_fscache set will be uncached.
 
 
-==============
-PAGE UNCACHING
+Page Uncaching
 ==============
 
-To uncache a page, this function should be called:
+To uncache a page, this function should be called::
 
        void fscache_uncache_page(struct fscache_cookie *cookie,
                                  struct page *page);
@@ -644,12 +637,12 @@ data file must be retired (see the relinquish cookie function below).
 
 Furthermore, note that this does not cancel the asynchronous read or write
 operation started by the read/alloc and write functions, so the page
-invalidation functions must use:
+invalidation functions must use::
 
        bool fscache_check_page_write(struct fscache_cookie *cookie,
                                      struct page *page);
 
-to see if a page is being written to the cache, and:
+to see if a page is being written to the cache, and::
 
        void fscache_wait_on_page_write(struct fscache_cookie *cookie,
                                        struct page *page);
@@ -660,7 +653,7 @@ to wait for it to finish if it is.
 When releasepage() is being implemented, a special FS-Cache function exists to
 manage the heuristics of coping with vmscan trying to eject pages, which may
 conflict with the cache trying to write pages to the cache (which may itself
-need to allocate memory):
+need to allocate memory)::
 
        bool fscache_maybe_release_page(struct fscache_cookie *cookie,
                                        struct page *page,
@@ -676,12 +669,12 @@ storage request to complete, or it may attempt to cancel the storage request -
 in which case the page will not be stored in the cache this time.
 
 
-BULK INODE PAGE UNCACHE
+Bulk Image Page Uncache
 -----------------------
 
 A convenience routine is provided to perform an uncache on all the pages
 attached to an inode.  This assumes that the pages on the inode correspond on a
-1:1 basis with the pages in the cache.
+1:1 basis with the pages in the cache::
 
        void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
                                             struct inode *inode);
@@ -692,12 +685,11 @@ written to the cache and for the cache to finish with the page generally.  No
 error is returned.
 
 
-===============================
-INDEX AND DATA FILE CONSISTENCY
+Index and Data File consistency
 ===============================
 
 To find out whether auxiliary data for an object is up to data within the
-cache, the following function can be called:
+cache, the following function can be called::
 
        int fscache_check_consistency(struct fscache_cookie *cookie,
                                      const void *aux_data);
@@ -708,7 +700,7 @@ data buffer first.  It returns 0 if it is and -ESTALE if it isn't; it may also
 return -ENOMEM and -ERESTARTSYS.
 
 To request an update of the index data for an index or other object, the
-following function should be called:
+following function should be called::
 
        void fscache_update_cookie(struct fscache_cookie *cookie,
                                   const void *aux_data);
@@ -721,8 +713,7 @@ Note that partial updates may happen automatically at other times, such as when
 data blocks are added to a data file object.
 
 
-=================
-COOKIE ENABLEMENT
+Cookie Enablement
 =================
 
 Cookies exist in one of two states: enabled and disabled.  If a cookie is
@@ -731,7 +722,7 @@ invalidate its state; allocate, read or write backing pages - though it is
 still possible to uncache pages and relinquish the cookie.
 
 The initial enablement state is set by fscache_acquire_cookie(), but the cookie
-can be enabled or disabled later.  To disable a cookie, call:
+can be enabled or disabled later.  To disable a cookie, call::
 
        void fscache_disable_cookie(struct fscache_cookie *cookie,
                                    const void *aux_data,
@@ -746,7 +737,7 @@ All possible failures are handled internally.  The caller should consider
 calling fscache_uncache_all_inode_pages() afterwards to make sure all page
 markings are cleared up.
 
-Cookies can be enabled or reenabled with:
+Cookies can be enabled or reenabled with::
 
        void fscache_enable_cookie(struct fscache_cookie *cookie,
                                   const void *aux_data,
@@ -771,13 +762,12 @@ In both cases, the cookie's auxiliary data buffer is updated from aux_data if
 that is non-NULL inside the enablement lock before proceeding.
 
 
-===============================
-MISCELLANEOUS COOKIE OPERATIONS
+Miscellaneous Cookie operations
 ===============================
 
 There are a number of operations that can be used to control cookies:
 
(*) Cookie pinning:
    * Cookie pinning::
 
        int fscache_pin_cookie(struct fscache_cookie *cookie);
        void fscache_unpin_cookie(struct fscache_cookie *cookie);
@@ -790,7 +780,7 @@ There are a number of operations that can be used to control cookies:
      -ENOSPC if there isn't enough space to honour the operation, -ENOMEM or
      -EIO if there's any other problem.
 
(*) Data space reservation:
  * Data space reservation::
 
        int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size);
 
@@ -809,11 +799,10 @@ There are a number of operations that can be used to control cookies:
      make space if it's not in use.
 
 
-=====================
-COOKIE UNREGISTRATION
+Cookie Unregistration
 =====================
 
-To get rid of a cookie, this function should be called.
+To get rid of a cookie, this function should be called::
 
        void fscache_relinquish_cookie(struct fscache_cookie *cookie,
                                       const void *aux_data,
@@ -835,16 +824,14 @@ the cookies for "child" indices, objects and pages have been relinquished
 first.
 
 
-==================
-INDEX INVALIDATION
+Index Invalidation
 ==================
 
 There is no direct way to invalidate an index subtree.  To do this, the caller
 should relinquish and retire the cookie they have, and then acquire a new one.
 
 
-======================
-DATA FILE INVALIDATION
+Data File Invalidation
 ======================
 
 Sometimes it will be necessary to invalidate an object that contains data.
@@ -853,7 +840,7 @@ change - at which point the netfs has to throw away all the state it had for an
 inode and reload from the server.
 
 To indicate that a cache object should be invalidated, the following function
-can be called:
+can be called::
 
        void fscache_invalidate(struct fscache_cookie *cookie);
 
@@ -868,13 +855,12 @@ auxiliary data update operation as it is very likely these will have changed.
 
 Using the following function, the netfs can wait for the invalidation operation
 to have reached a point at which it can start submitting ordinary operations
-once again:
+once again::
 
        void fscache_wait_on_invalidate(struct fscache_cookie *cookie);
 
 
-===========================
-FS-CACHE SPECIFIC PAGE FLAG
+FS-cache Specific Page Flag
 ===========================
 
 FS-Cache makes use of a page flag, PG_private_2, for its own purpose.  This is
@@ -898,7 +884,7 @@ was given under certain circumstances.
 This bit does not overlap with such as PG_private.  This means that FS-Cache
 can be used with a filesystem that uses the block buffering code.
 
-There are a number of operations defined on this flag:
+There are a number of operations defined on this flag::
 
        int PageFsCache(struct page *page);
        void SetPageFsCache(struct page *page)
similarity index 95%
rename from Documentation/filesystems/caching/object.txt
rename to Documentation/filesystems/caching/object.rst
index 100ff41127e436ac8614c6c095a4e56dff2ccb62..ce0e043ccd33b182b72613b4561e28a2ee5a5fa7 100644 (file)
@@ -1,10 +1,12 @@
-            ====================================================
-            IN-KERNEL CACHE OBJECT REPRESENTATION AND MANAGEMENT
-            ====================================================
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================================
+In-Kernel Cache Object Representation and Management
+====================================================
 
 By: David Howells <dhowells@redhat.com>
 
-Contents:
+.. Contents:
 
  (*) Representation
 
@@ -18,8 +20,7 @@ Contents:
  (*) The set of events.
 
 
-==============
-REPRESENTATION
+Representation
 ==============
 
 FS-Cache maintains an in-kernel representation of each object that a netfs is
@@ -38,7 +39,7 @@ or even by no objects (it may not be cached).
 
 Furthermore, both cookies and objects are hierarchical.  The two hierarchies
 correspond, but the cookies tree is a superset of the union of the object trees
-of multiple caches:
+of multiple caches::
 
            NETFS INDEX TREE               :      CACHE 1     :      CACHE 2
                                           :                  :
@@ -89,8 +90,7 @@ pointers to the cookies.  The cookies themselves and any objects attached to
 those cookies are hidden from it.
 
 
-===============================
-OBJECT MANAGEMENT STATE MACHINE
+Object Management State Machine
 ===============================
 
 Within FS-Cache, each active object is managed by its own individual state
@@ -124,7 +124,7 @@ is not masked, the object will be queued for processing (by calling
 fscache_enqueue_object()).
 
 
-PROVISION OF CPU TIME
+Provision of CPU Time
 ---------------------
 
 The work to be done by the various states was given CPU time by the threads of
@@ -141,7 +141,7 @@ because:
      workqueues don't necessarily have the right numbers of threads.
 
 
-LOCKING SIMPLIFICATION
+Locking Simplification
 ----------------------
 
 Because only one worker thread may be operating on any particular object's
@@ -151,8 +151,7 @@ from the cache backend's representation (fscache_object) - which may be
 requested from either end.
 
 
-=================
-THE SET OF STATES
+The Set of States
 =================
 
 The object state machine has a set of states that it can be in.  There are
@@ -275,19 +274,17 @@ memory and potentially deletes stuff from disk:
      this state.
 
 
-THE SET OF EVENTS
+The Set of Events
 -----------------
 
 There are a number of events that can be raised to an object state machine:
 
- (*) FSCACHE_OBJECT_EV_UPDATE
-
+ FSCACHE_OBJECT_EV_UPDATE
      The netfs requested that an object be updated.  The state machine will ask
      the cache backend to update the object, and the cache backend will ask the
      netfs for details of the change through its cookie definition ops.
 
- (*) FSCACHE_OBJECT_EV_CLEARED
-
+ FSCACHE_OBJECT_EV_CLEARED
      This is signalled in two circumstances:
 
      (a) when an object's last child object is dropped and
@@ -296,20 +293,16 @@ There are a number of events that can be raised to an object state machine:
 
      This is used to proceed from the dying state.
 
- (*) FSCACHE_OBJECT_EV_ERROR
-
+ FSCACHE_OBJECT_EV_ERROR
      This is signalled when an I/O error occurs during the processing of some
      object.
 
- (*) FSCACHE_OBJECT_EV_RELEASE
- (*) FSCACHE_OBJECT_EV_RETIRE
-
+ FSCACHE_OBJECT_EV_RELEASE, FSCACHE_OBJECT_EV_RETIRE
      These are signalled when the netfs relinquishes a cookie it was using.
      The event selected depends on whether the netfs asks for the backing
      object to be retired (deleted) or retained.
 
- (*) FSCACHE_OBJECT_EV_WITHDRAW
-
+ FSCACHE_OBJECT_EV_WITHDRAW
      This is signalled when the cache backend wants to withdraw an object.
      This means that the object will have to be detached from the netfs's
      cookie.
similarity index 90%
rename from Documentation/filesystems/caching/operations.txt
rename to Documentation/filesystems/caching/operations.rst
index d8976c434718fe46510a5060b900505b220cf6f5..f7ddcc028939187533311772da44132399e11114 100644 (file)
@@ -1,10 +1,12 @@
-                      ================================
-                      ASYNCHRONOUS OPERATIONS HANDLING
-                      ================================
+.. SPDX-License-Identifier: GPL-2.0
+
+================================
+Asynchronous Operations Handling
+================================
 
 By: David Howells <dhowells@redhat.com>
 
-Contents:
+.. Contents:
 
  (*) Overview.
 
@@ -17,8 +19,7 @@ Contents:
  (*) Asynchronous callback.
 
 
-========
-OVERVIEW
+Overview
 ========
 
 FS-Cache has an asynchronous operations handling facility that it uses for its
@@ -33,11 +34,10 @@ backend for completion.
 To make use of this facility, <linux/fscache-cache.h> should be #included.
 
 
-===============================
-OPERATION RECORD INITIALISATION
+Operation Record Initialisation
 ===============================
 
-An operation is recorded in an fscache_operation struct:
+An operation is recorded in an fscache_operation struct::
 
        struct fscache_operation {
                union {
@@ -50,7 +50,7 @@ An operation is recorded in an fscache_operation struct:
        };
 
 Someone wanting to issue an operation should allocate something with this
-struct embedded in it.  They should initialise it by calling:
+struct embedded in it.  They should initialise it by calling::
 
        void fscache_operation_init(struct fscache_operation *op,
                                    fscache_operation_release_t release);
@@ -67,8 +67,7 @@ FSCACHE_OP_WAITING may be set in op->flags prior to each submission of the
 operation and waited for afterwards.
 
 
-==========
-PARAMETERS
+Parameters
 ==========
 
 There are a number of parameters that can be set in the operation record's flag
@@ -87,7 +86,7 @@ operations:
 
      If this option is to be used, FSCACHE_OP_WAITING must be set in op->flags
      before submitting the operation, and the operating thread must wait for it
-     to be cleared before proceeding:
+     to be cleared before proceeding::
 
                wait_on_bit(&op->flags, FSCACHE_OP_WAITING,
                            TASK_UNINTERRUPTIBLE);
@@ -101,7 +100,7 @@ operations:
      page to a netfs page after the backing fs has read the page in.
 
      If this option is used, op->fast_work and op->processor must be
-     initialised before submitting the operation:
+     initialised before submitting the operation::
 
                INIT_WORK(&op->fast_work, do_some_work);
 
@@ -114,7 +113,7 @@ operations:
      pages that have just been fetched from a remote server.
 
      If this option is used, op->slow_work and op->processor must be
-     initialised before submitting the operation:
+     initialised before submitting the operation::
 
                fscache_operation_init_slow(op, processor)
 
@@ -132,8 +131,7 @@ Furthermore, operations may be one of two types:
      operations running at the same time.
 
 
-=========
-PROCEDURE
+Procedure
 =========
 
 Operations are used through the following procedure:
@@ -143,7 +141,7 @@ Operations are used through the following procedure:
      generic op embedded within.
 
  (2) The submitting thread must then submit the operation for processing using
-     one of the following two functions:
+     one of the following two functions::
 
        int fscache_submit_op(struct fscache_object *object,
                              struct fscache_operation *op);
@@ -164,7 +162,7 @@ Operations are used through the following procedure:
      operation of conflicting exclusivity is in progress on the object.
 
      If the operation is asynchronous, the manager will retain a reference to
-     it, so the caller should put their reference to it by passing it to:
+     it, so the caller should put their reference to it by passing it to::
 
        void fscache_put_operation(struct fscache_operation *op);
 
@@ -179,12 +177,12 @@ Operations are used through the following procedure:
  (4) The operation holds an effective lock upon the object, preventing other
      exclusive ops conflicting until it is released.  The operation can be
      enqueued for further immediate asynchronous processing by adjusting the
-     CPU time provisioning option if necessary, eg:
+     CPU time provisioning option if necessary, eg::
 
        op->flags &= ~FSCACHE_OP_TYPE;
        op->flags |= ~FSCACHE_OP_FAST;
 
-     and calling:
+     and calling::
 
        void fscache_enqueue_operation(struct fscache_operation *op)
 
@@ -192,13 +190,12 @@ Operations are used through the following procedure:
      pools.
 
 
-=====================
-ASYNCHRONOUS CALLBACK
+Asynchronous Callback
 =====================
 
 When used in asynchronous mode, the worker thread pool will invoke the
 processor method with a pointer to the operation.  This should then get at the
-container struct by using container_of():
+container struct by using container_of()::
 
        static void fscache_write_op(struct fscache_operation *_op)
        {
similarity index 72%
rename from Documentation/filesystems/cifs/cifsroot.txt
rename to Documentation/filesystems/cifs/cifsroot.rst
index 947b7ec6ce9e0f9d0d601b7b49bb7399fde09492..4930bb443134aecdc4445b8058d8272bb11aa696 100644 (file)
@@ -1,7 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================================
 Mounting root file system via SMB (cifs.ko)
 ===========================================
 
 Written 2019 by Paulo Alcantara <palcantara@suse.de>
+
 Written 2019 by Aurelien Aptel <aaptel@suse.com>
 
 The CONFIG_CIFS_ROOT option enables experimental root file system
@@ -32,7 +36,7 @@ Server configuration
 ====================
 
 To enable SMB1+UNIX extensions you will need to set these global
-settings in Samba smb.conf:
+settings in Samba smb.conf::
 
     [global]
     server min protocol = NT1
@@ -41,12 +45,16 @@ settings in Samba smb.conf:
 Kernel command line
 ===================
 
-root=/dev/cifs
+::
+
+    root=/dev/cifs
 
 This is just a virtual device that basically tells the kernel to mount
 the root file system via SMB protocol.
 
-cifsroot=//<server-ip>/<share>[,options]
+::
+
+    cifsroot=//<server-ip>/<share>[,options]
 
 Enables the kernel to mount the root file system via SMB that are
 located in the <server-ip> and <share> specified in this option.
@@ -65,33 +73,33 @@ options
 Examples
 ========
 
-Export root file system as a Samba share in smb.conf file.
+Export root file system as a Samba share in smb.conf file::
 
-...
-[linux]
-       path = /path/to/rootfs
-       read only = no
-       guest ok = yes
-       force user = root
-       force group = root
-       browseable = yes
-       writeable = yes
-       admin users = root
-       public = yes
-       create mask = 0777
-       directory mask = 0777
-...
+    ...
+    [linux]
+           path = /path/to/rootfs
+           read only = no
+           guest ok = yes
+           force user = root
+           force group = root
+           browseable = yes
+           writeable = yes
+           admin users = root
+           public = yes
+           create mask = 0777
+           directory mask = 0777
+    ...
 
-Restart smb service.
+Restart smb service::
 
-# systemctl restart smb
+    # systemctl restart smb
 
 Test it under QEMU on a kernel built with CONFIG_CIFS_ROOT and
-CONFIG_IP_PNP options enabled.
+CONFIG_IP_PNP options enabled::
 
-# qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \
-  -kernel /path/to/linux/arch/x86/boot/bzImage -nographic \
-  -append "root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/linux,username=foo,password=bar console=ttyS0 3"
+    # qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \
+    -kernel /path/to/linux/arch/x86/boot/bzImage -nographic \
+    -append "root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/linux,username=foo,password=bar console=ttyS0 3"
 
 
 1: https://wiki.samba.org/index.php/UNIX_Extensions
diff --git a/Documentation/filesystems/coda.rst b/Documentation/filesystems/coda.rst
new file mode 100644 (file)
index 0000000..84c860c
--- /dev/null
@@ -0,0 +1,1670 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+Coda Kernel-Venus Interface
+===========================
+
+.. Note::
+
+   This is one of the technical documents describing a component of
+   Coda -- this document describes the client kernel-Venus interface.
+
+For more information:
+
+  http://www.coda.cs.cmu.edu
+
+For user level software needed to run Coda:
+
+  ftp://ftp.coda.cs.cmu.edu
+
+To run Coda you need to get a user level cache manager for the client,
+named Venus, as well as tools to manipulate ACLs, to log in, etc.  The
+client needs to have the Coda filesystem selected in the kernel
+configuration.
+
+The server needs a user level server and at present does not depend on
+kernel support.
+
+  The Venus kernel interface
+
+  Peter J. Braam
+
+  v1.0, Nov 9, 1997
+
+  This document describes the communication between Venus and kernel
+  level filesystem code needed for the operation of the Coda file sys-
+  tem.  This document version is meant to describe the current interface
+  (version 1.0) as well as improvements we envisage.
+
+.. Table of Contents
+
+  1. Introduction
+
+  2. Servicing Coda filesystem calls
+
+  3. The message layer
+
+     3.1 Implementation details
+
+  4. The interface at the call level
+
+     4.1 Data structures shared by the kernel and Venus
+     4.2 The pioctl interface
+     4.3 root
+     4.4 lookup
+     4.5 getattr
+     4.6 setattr
+     4.7 access
+     4.8 create
+     4.9 mkdir
+     4.10 link
+     4.11 symlink
+     4.12 remove
+     4.13 rmdir
+     4.14 readlink
+     4.15 open
+     4.16 close
+     4.17 ioctl
+     4.18 rename
+     4.19 readdir
+     4.20 vget
+     4.21 fsync
+     4.22 inactive
+     4.23 rdwr
+     4.24 odymount
+     4.25 ody_lookup
+     4.26 ody_expand
+     4.27 prefetch
+     4.28 signal
+
+  5. The minicache and downcalls
+
+     5.1 INVALIDATE
+     5.2 FLUSH
+     5.3 PURGEUSER
+     5.4 ZAPFILE
+     5.5 ZAPDIR
+     5.6 ZAPVNODE
+     5.7 PURGEFID
+     5.8 REPLACE
+
+  6. Initialization and cleanup
+
+     6.1 Requirements
+
+1. Introduction
+===============
+
+  A key component in the Coda Distributed File System is the cache
+  manager, Venus.
+
+  When processes on a Coda enabled system access files in the Coda
+  filesystem, requests are directed at the filesystem layer in the
+  operating system. The operating system will communicate with Venus to
+  service the request for the process.  Venus manages a persistent
+  client cache and makes remote procedure calls to Coda file servers and
+  related servers (such as authentication servers) to service these
+  requests it receives from the operating system.  When Venus has
+  serviced a request it replies to the operating system with appropriate
+  return codes, and other data related to the request.  Optionally the
+  kernel support for Coda may maintain a minicache of recently processed
+  requests to limit the number of interactions with Venus.  Venus
+  possesses the facility to inform the kernel when elements from its
+  minicache are no longer valid.
+
+  This document describes precisely this communication between the
+  kernel and Venus.  The definitions of so called upcalls and downcalls
+  will be given with the format of the data they handle. We shall also
+  describe the semantic invariants resulting from the calls.
+
+  Historically Coda was implemented in a BSD file system in Mach 2.6.
+  The interface between the kernel and Venus is very similar to the BSD
+  VFS interface.  Similar functionality is provided, and the format of
+  the parameters and returned data is very similar to the BSD VFS.  This
+  leads to an almost natural environment for implementing a kernel-level
+  filesystem driver for Coda in a BSD system.  However, other operating
+  systems such as Linux and Windows 95 and NT have virtual filesystem
+  with different interfaces.
+
+  To implement Coda on these systems some reverse engineering of the
+  Venus/Kernel protocol is necessary.  Also it came to light that other
+  systems could profit significantly from certain small optimizations
+  and modifications to the protocol. To facilitate this work as well as
+  to make future ports easier, communication between Venus and the
+  kernel should be documented in great detail.  This is the aim of this
+  document.
+
+2.  Servicing Coda filesystem calls
+===================================
+
+  The service of a request for a Coda file system service originates in
+  a process P which accessing a Coda file. It makes a system call which
+  traps to the OS kernel. Examples of such calls trapping to the kernel
+  are ``read``, ``write``, ``open``, ``close``, ``create``, ``mkdir``,
+  ``rmdir``, ``chmod`` in a Unix ontext.  Similar calls exist in the Win32
+  environment, and are named ``CreateFile``.
+
+  Generally the operating system handles the request in a virtual
+  filesystem (VFS) layer, which is named I/O Manager in NT and IFS
+  manager in Windows 95.  The VFS is responsible for partial processing
+  of the request and for locating the specific filesystem(s) which will
+  service parts of the request.  Usually the information in the path
+  assists in locating the correct FS drivers.  Sometimes after extensive
+  pre-processing, the VFS starts invoking exported routines in the FS
+  driver.  This is the point where the FS specific processing of the
+  request starts, and here the Coda specific kernel code comes into
+  play.
+
+  The FS layer for Coda must expose and implement several interfaces.
+  First and foremost the VFS must be able to make all necessary calls to
+  the Coda FS layer, so the Coda FS driver must expose the VFS interface
+  as applicable in the operating system. These differ very significantly
+  among operating systems, but share features such as facilities to
+  read/write and create and remove objects.  The Coda FS layer services
+  such VFS requests by invoking one or more well defined services
+  offered by the cache manager Venus.  When the replies from Venus have
+  come back to the FS driver, servicing of the VFS call continues and
+  finishes with a reply to the kernel's VFS. Finally the VFS layer
+  returns to the process.
+
+  As a result of this design a basic interface exposed by the FS driver
+  must allow Venus to manage message traffic.  In particular Venus must
+  be able to retrieve and place messages and to be notified of the
+  arrival of a new message. The notification must be through a mechanism
+  which does not block Venus since Venus must attend to other tasks even
+  when no messages are waiting or being processed.
+
+  **Interfaces of the Coda FS Driver**
+
+  Furthermore the FS layer provides for a special path of communication
+  between a user process and Venus, called the pioctl interface. The
+  pioctl interface is used for Coda specific services, such as
+  requesting detailed information about the persistent cache managed by
+  Venus. Here the involvement of the kernel is minimal.  It identifies
+  the calling process and passes the information on to Venus.  When
+  Venus replies the response is passed back to the caller in unmodified
+  form.
+
+  Finally Venus allows the kernel FS driver to cache the results from
+  certain services.  This is done to avoid excessive context switches
+  and results in an efficient system.  However, Venus may acquire
+  information, for example from the network which implies that cached
+  information must be flushed or replaced. Venus then makes a downcall
+  to the Coda FS layer to request flushes or updates in the cache.  The
+  kernel FS driver handles such requests synchronously.
+
+  Among these interfaces the VFS interface and the facility to place,
+  receive and be notified of messages are platform specific.  We will
+  not go into the calls exported to the VFS layer but we will state the
+  requirements of the message exchange mechanism.
+
+
+3.  The message layer
+=====================
+
+  At the lowest level the communication between Venus and the FS driver
+  proceeds through messages.  The synchronization between processes
+  requesting Coda file service and Venus relies on blocking and waking
+  up processes.  The Coda FS driver processes VFS- and pioctl-requests
+  on behalf of a process P, creates messages for Venus, awaits replies
+  and finally returns to the caller.  The implementation of the exchange
+  of messages is platform specific, but the semantics have (so far)
+  appeared to be generally applicable.  Data buffers are created by the
+  FS Driver in kernel memory on behalf of P and copied to user memory in
+  Venus.
+
+  The FS Driver while servicing P makes upcalls to Venus.  Such an
+  upcall is dispatched to Venus by creating a message structure.  The
+  structure contains the identification of P, the message sequence
+  number, the size of the request and a pointer to the data in kernel
+  memory for the request.  Since the data buffer is re-used to hold the
+  reply from Venus, there is a field for the size of the reply.  A flags
+  field is used in the message to precisely record the status of the
+  message.  Additional platform dependent structures involve pointers to
+  determine the position of the message on queues and pointers to
+  synchronization objects.  In the upcall routine the message structure
+  is filled in, flags are set to 0, and it is placed on the *pending*
+  queue.  The routine calling upcall is responsible for allocating the
+  data buffer; its structure will be described in the next section.
+
+  A facility must exist to notify Venus that the message has been
+  created, and implemented using available synchronization objects in
+  the OS. This notification is done in the upcall context of the process
+  P. When the message is on the pending queue, process P cannot proceed
+  in upcall.  The (kernel mode) processing of P in the filesystem
+  request routine must be suspended until Venus has replied.  Therefore
+  the calling thread in P is blocked in upcall.  A pointer in the
+  message structure will locate the synchronization object on which P is
+  sleeping.
+
+  Venus detects the notification that a message has arrived, and the FS
+  driver allow Venus to retrieve the message with a getmsg_from_kernel
+  call. This action finishes in the kernel by putting the message on the
+  queue of processing messages and setting flags to READ.  Venus is
+  passed the contents of the data buffer. The getmsg_from_kernel call
+  now returns and Venus processes the request.
+
+  At some later point the FS driver receives a message from Venus,
+  namely when Venus calls sendmsg_to_kernel.  At this moment the Coda FS
+  driver looks at the contents of the message and decides if:
+
+
+  *  the message is a reply for a suspended thread P.  If so it removes
+     the message from the processing queue and marks the message as
+     WRITTEN.  Finally, the FS driver unblocks P (still in the kernel
+     mode context of Venus) and the sendmsg_to_kernel call returns to
+     Venus.  The process P will be scheduled at some point and continues
+     processing its upcall with the data buffer replaced with the reply
+     from Venus.
+
+  *  The message is a ``downcall``.  A downcall is a request from Venus to
+     the FS Driver. The FS driver processes the request immediately
+     (usually a cache eviction or replacement) and when it finishes
+     sendmsg_to_kernel returns.
+
+  Now P awakes and continues processing upcall.  There are some
+  subtleties to take account of. First P will determine if it was woken
+  up in upcall by a signal from some other source (for example an
+  attempt to terminate P) or as is normally the case by Venus in its
+  sendmsg_to_kernel call.  In the normal case, the upcall routine will
+  deallocate the message structure and return.  The FS routine can proceed
+  with its processing.
+
+
+  **Sleeping and IPC arrangements**
+
+  In case P is woken up by a signal and not by Venus, it will first look
+  at the flags field.  If the message is not yet READ, the process P can
+  handle its signal without notifying Venus.  If Venus has READ, and
+  the request should not be processed, P can send Venus a signal message
+  to indicate that it should disregard the previous message.  Such
+  signals are put in the queue at the head, and read first by Venus.  If
+  the message is already marked as WRITTEN it is too late to stop the
+  processing.  The VFS routine will now continue.  (-- If a VFS request
+  involves more than one upcall, this can lead to complicated state, an
+  extra field "handle_signals" could be added in the message structure
+  to indicate points of no return have been passed.--)
+
+
+
+3.1.  Implementation details
+----------------------------
+
+  The Unix implementation of this mechanism has been through the
+  implementation of a character device associated with Coda.  Venus
+  retrieves messages by doing a read on the device, replies are sent
+  with a write and notification is through the select system call on the
+  file descriptor for the device.  The process P is kept waiting on an
+  interruptible wait queue object.
+
+  In Windows NT and the DPMI Windows 95 implementation a DeviceIoControl
+  call is used.  The DeviceIoControl call is designed to copy buffers
+  from user memory to kernel memory with OPCODES. The sendmsg_to_kernel
+  is issued as a synchronous call, while the getmsg_from_kernel call is
+  asynchronous.  Windows EventObjects are used for notification of
+  message arrival.  The process P is kept waiting on a KernelEvent
+  object in NT and a semaphore in Windows 95.
+
+
+4.  The interface at the call level
+===================================
+
+
+  This section describes the upcalls a Coda FS driver can make to Venus.
+  Each of these upcalls make use of two structures: inputArgs and
+  outputArgs.   In pseudo BNF form the structures take the following
+  form::
+
+
+       struct inputArgs {
+           u_long opcode;
+           u_long unique;     /* Keep multiple outstanding msgs distinct */
+           u_short pid;                 /* Common to all */
+           u_short pgid;                /* Common to all */
+           struct CodaCred cred;        /* Common to all */
+
+           <union "in" of call dependent parts of inputArgs>
+       };
+
+       struct outputArgs {
+           u_long opcode;
+           u_long unique;       /* Keep multiple outstanding msgs distinct */
+           u_long result;
+
+           <union "out" of call dependent parts of inputArgs>
+       };
+
+
+
+  Before going on let us elucidate the role of the various fields. The
+  inputArgs start with the opcode which defines the type of service
+  requested from Venus. There are approximately 30 upcalls at present
+  which we will discuss.   The unique field labels the inputArg with a
+  unique number which will identify the message uniquely.  A process and
+  process group id are passed.  Finally the credentials of the caller
+  are included.
+
+  Before delving into the specific calls we need to discuss a variety of
+  data structures shared by the kernel and Venus.
+
+
+
+
+4.1.  Data structures shared by the kernel and Venus
+----------------------------------------------------
+
+
+  The CodaCred structure defines a variety of user and group ids as
+  they are set for the calling process. The vuid_t and vgid_t are 32 bit
+  unsigned integers.  It also defines group membership in an array.  On
+  Unix the CodaCred has proven sufficient to implement good security
+  semantics for Coda but the structure may have to undergo modification
+  for the Windows environment when these mature::
+
+       struct CodaCred {
+           vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, effective, set, fs uid */
+           vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+           vgid_t cr_groups[NGROUPS];        /* Group membership for caller */
+       };
+
+
+  .. Note::
+
+     It is questionable if we need CodaCreds in Venus. Finally Venus
+     doesn't know about groups, although it does create files with the
+     default uid/gid.  Perhaps the list of group membership is superfluous.
+
+
+  The next item is the fundamental identifier used to identify Coda
+  files, the ViceFid.  A fid of a file uniquely defines a file or
+  directory in the Coda filesystem within a cell [1]_::
+
+       typedef struct ViceFid {
+           VolumeId Volume;
+           VnodeId Vnode;
+           Unique_t Unique;
+       } ViceFid;
+
+  .. [1] A cell is agroup of Coda servers acting under the aegis of a single
+        system control machine or SCM. See the Coda Administration manual
+        for a detailed description of the role of the SCM.
+
+  Each of the constituent fields: VolumeId, VnodeId and Unique_t are
+  unsigned 32 bit integers.  We envisage that a further field will need
+  to be prefixed to identify the Coda cell; this will probably take the
+  form of a Ipv6 size IP address naming the Coda cell through DNS.
+
+  The next important structure shared between Venus and the kernel is
+  the attributes of the file.  The following structure is used to
+  exchange information.  It has room for future extensions such as
+  support for device files (currently not present in Coda)::
+
+
+       struct coda_timespec {
+               int64_t         tv_sec;         /* seconds */
+               long            tv_nsec;        /* nanoseconds */
+       };
+
+       struct coda_vattr {
+               enum coda_vtype va_type;        /* vnode type (for create) */
+               u_short         va_mode;        /* files access mode and type */
+               short           va_nlink;       /* number of references to file */
+               vuid_t          va_uid;         /* owner user id */
+               vgid_t          va_gid;         /* owner group id */
+               long            va_fsid;        /* file system id (dev for now) */
+               long            va_fileid;      /* file id */
+               u_quad_t        va_size;        /* file size in bytes */
+               long            va_blocksize;   /* blocksize preferred for i/o */
+               struct coda_timespec va_atime;  /* time of last access */
+               struct coda_timespec va_mtime;  /* time of last modification */
+               struct coda_timespec va_ctime;  /* time file changed */
+               u_long          va_gen;         /* generation number of file */
+               u_long          va_flags;       /* flags defined for file */
+               dev_t           va_rdev;        /* device special file represents */
+               u_quad_t        va_bytes;       /* bytes of disk space held by file */
+               u_quad_t        va_filerev;     /* file modification number */
+               u_int           va_vaflags;     /* operations flags, see below */
+               long            va_spare;       /* remain quad aligned */
+       };
+
+
+4.2.  The pioctl interface
+--------------------------
+
+
+  Coda specific requests can be made by application through the pioctl
+  interface. The pioctl is implemented as an ordinary ioctl on a
+  fictitious file /coda/.CONTROL.  The pioctl call opens this file, gets
+  a file handle and makes the ioctl call. Finally it closes the file.
+
+  The kernel involvement in this is limited to providing the facility to
+  open and close and pass the ioctl message and to verify that a path in
+  the pioctl data buffers is a file in a Coda filesystem.
+
+  The kernel is handed a data packet of the form::
+
+       struct {
+           const char *path;
+           struct ViceIoctl vidata;
+           int follow;
+       } data;
+
+
+
+  where::
+
+
+       struct ViceIoctl {
+               caddr_t in, out;        /* Data to be transferred in, or out */
+               short in_size;          /* Size of input buffer <= 2K */
+               short out_size;         /* Maximum size of output buffer, <= 2K */
+       };
+
+
+
+  The path must be a Coda file, otherwise the ioctl upcall will not be
+  made.
+
+  .. Note:: The data structures and code are a mess.  We need to clean this up.
+
+
+**We now proceed to document the individual calls**:
+
+
+4.3.  root
+----------
+
+
+  Arguments
+     in
+
+       empty
+
+     out::
+
+               struct cfs_root_out {
+                   ViceFid VFid;
+               } cfs_root;
+
+
+
+  Description
+    This call is made to Venus during the initialization of
+    the Coda filesystem. If the result is zero, the cfs_root structure
+    contains the ViceFid of the root of the Coda filesystem. If a non-zero
+    result is generated, its value is a platform dependent error code
+    indicating the difficulty Venus encountered in locating the root of
+    the Coda filesystem.
+
+4.4.  lookup
+------------
+
+
+  Summary
+    Find the ViceFid and type of an object in a directory if it exists.
+
+  Arguments
+     in::
+
+               struct  cfs_lookup_in {
+                   ViceFid     VFid;
+                   char        *name;          /* Place holder for data. */
+               } cfs_lookup;
+
+
+
+     out::
+
+               struct cfs_lookup_out {
+                   ViceFid VFid;
+                   int vtype;
+               } cfs_lookup;
+
+
+
+  Description
+    This call is made to determine the ViceFid and filetype of
+    a directory entry.  The directory entry requested carries name name
+    and Venus will search the directory identified by cfs_lookup_in.VFid.
+    The result may indicate that the name does not exist, or that
+    difficulty was encountered in finding it (e.g. due to disconnection).
+    If the result is zero, the field cfs_lookup_out.VFid contains the
+    targets ViceFid and cfs_lookup_out.vtype the coda_vtype giving the
+    type of object the name designates.
+
+  The name of the object is an 8 bit character string of maximum length
+  CFS_MAXNAMLEN, currently set to 256 (including a 0 terminator.)
+
+  It is extremely important to realize that Venus bitwise ors the field
+  cfs_lookup.vtype with CFS_NOCACHE to indicate that the object should
+  not be put in the kernel name cache.
+
+  .. Note::
+
+     The type of the vtype is currently wrong.  It should be
+     coda_vtype. Linux does not take note of CFS_NOCACHE.  It should.
+
+
+4.5.  getattr
+-------------
+
+
+  Summary Get the attributes of a file.
+
+  Arguments
+     in::
+
+               struct cfs_getattr_in {
+                   ViceFid VFid;
+                   struct coda_vattr attr; /* XXXXX */
+               } cfs_getattr;
+
+
+
+     out::
+
+               struct cfs_getattr_out {
+                   struct coda_vattr attr;
+               } cfs_getattr;
+
+
+
+  Description
+    This call returns the attributes of the file identified by fid.
+
+  Errors
+    Errors can occur if the object with fid does not exist, is
+    unaccessible or if the caller does not have permission to fetch
+    attributes.
+
+  .. Note::
+
+     Many kernel FS drivers (Linux, NT and Windows 95) need to acquire
+     the attributes as well as the Fid for the instantiation of an internal
+     "inode" or "FileHandle".  A significant improvement in performance on
+     such systems could be made by combining the lookup and getattr calls
+     both at the Venus/kernel interaction level and at the RPC level.
+
+  The vattr structure included in the input arguments is superfluous and
+  should be removed.
+
+
+4.6.  setattr
+-------------
+
+
+  Summary
+    Set the attributes of a file.
+
+  Arguments
+     in::
+
+               struct cfs_setattr_in {
+                   ViceFid VFid;
+                   struct coda_vattr attr;
+               } cfs_setattr;
+
+
+
+
+     out
+
+       empty
+
+  Description
+    The structure attr is filled with attributes to be changed
+    in BSD style.  Attributes not to be changed are set to -1, apart from
+    vtype which is set to VNON. Other are set to the value to be assigned.
+    The only attributes which the FS driver may request to change are the
+    mode, owner, groupid, atime, mtime and ctime.  The return value
+    indicates success or failure.
+
+  Errors
+    A variety of errors can occur.  The object may not exist, may
+    be inaccessible, or permission may not be granted by Venus.
+
+
+4.7.  access
+------------
+
+
+  Arguments
+     in::
+
+               struct cfs_access_in {
+                   ViceFid     VFid;
+                   int flags;
+               } cfs_access;
+
+
+
+     out
+
+       empty
+
+  Description
+    Verify if access to the object identified by VFid for
+    operations described by flags is permitted.  The result indicates if
+    access will be granted.  It is important to remember that Coda uses
+    ACLs to enforce protection and that ultimately the servers, not the
+    clients enforce the security of the system.  The result of this call
+    will depend on whether a token is held by the user.
+
+  Errors
+    The object may not exist, or the ACL describing the protection
+    may not be accessible.
+
+
+4.8.  create
+------------
+
+
+  Summary
+    Invoked to create a file
+
+  Arguments
+     in::
+
+               struct cfs_create_in {
+                   ViceFid VFid;
+                   struct coda_vattr attr;
+                   int excl;
+                   int mode;
+                   char        *name;          /* Place holder for data. */
+               } cfs_create;
+
+
+
+
+     out::
+
+               struct cfs_create_out {
+                   ViceFid VFid;
+                   struct coda_vattr attr;
+               } cfs_create;
+
+
+
+  Description
+    This upcall is invoked to request creation of a file.
+    The file will be created in the directory identified by VFid, its name
+    will be name, and the mode will be mode.  If excl is set an error will
+    be returned if the file already exists.  If the size field in attr is
+    set to zero the file will be truncated.  The uid and gid of the file
+    are set by converting the CodaCred to a uid using a macro CRTOUID
+    (this macro is platform dependent).  Upon success the VFid and
+    attributes of the file are returned.  The Coda FS Driver will normally
+    instantiate a vnode, inode or file handle at kernel level for the new
+    object.
+
+
+  Errors
+    A variety of errors can occur. Permissions may be insufficient.
+    If the object exists and is not a file the error EISDIR is returned
+    under Unix.
+
+  .. Note::
+
+     The packing of parameters is very inefficient and appears to
+     indicate confusion between the system call creat and the VFS operation
+     create. The VFS operation create is only called to create new objects.
+     This create call differs from the Unix one in that it is not invoked
+     to return a file descriptor. The truncate and exclusive options,
+     together with the mode, could simply be part of the mode as it is
+     under Unix.  There should be no flags argument; this is used in open
+     (2) to return a file descriptor for READ or WRITE mode.
+
+  The attributes of the directory should be returned too, since the size
+  and mtime changed.
+
+
+4.9.  mkdir
+-----------
+
+
+  Summary
+    Create a new directory.
+
+  Arguments
+     in::
+
+               struct cfs_mkdir_in {
+                   ViceFid     VFid;
+                   struct coda_vattr attr;
+                   char        *name;          /* Place holder for data. */
+               } cfs_mkdir;
+
+
+
+     out::
+
+               struct cfs_mkdir_out {
+                   ViceFid VFid;
+                   struct coda_vattr attr;
+               } cfs_mkdir;
+
+
+
+
+  Description
+    This call is similar to create but creates a directory.
+    Only the mode field in the input parameters is used for creation.
+    Upon successful creation, the attr returned contains the attributes of
+    the new directory.
+
+  Errors
+    As for create.
+
+  .. Note::
+
+     The input parameter should be changed to mode instead of
+     attributes.
+
+  The attributes of the parent should be returned since the size and
+  mtime changes.
+
+
+4.10.  link
+-----------
+
+
+  Summary
+    Create a link to an existing file.
+
+  Arguments
+     in::
+
+               struct cfs_link_in {
+                   ViceFid sourceFid;          /* cnode to link *to* */
+                   ViceFid destFid;            /* Directory in which to place link */
+                   char        *tname;         /* Place holder for data. */
+               } cfs_link;
+
+
+
+     out
+
+       empty
+
+  Description
+    This call creates a link to the sourceFid in the directory
+    identified by destFid with name tname.  The source must reside in the
+    target's parent, i.e. the source must be have parent destFid, i.e. Coda
+    does not support cross directory hard links.  Only the return value is
+    relevant.  It indicates success or the type of failure.
+
+  Errors
+    The usual errors can occur.
+
+
+4.11.  symlink
+--------------
+
+
+  Summary
+    create a symbolic link
+
+  Arguments
+     in::
+
+               struct cfs_symlink_in {
+                   ViceFid     VFid;          /* Directory to put symlink in */
+                   char        *srcname;
+                   struct coda_vattr attr;
+                   char        *tname;
+               } cfs_symlink;
+
+
+
+     out
+
+       none
+
+  Description
+    Create a symbolic link. The link is to be placed in the
+    directory identified by VFid and named tname.  It should point to the
+    pathname srcname.  The attributes of the newly created object are to
+    be set to attr.
+
+  .. Note::
+
+     The attributes of the target directory should be returned since
+     its size changed.
+
+
+4.12.  remove
+-------------
+
+
+  Summary
+    Remove a file
+
+  Arguments
+     in::
+
+               struct cfs_remove_in {
+                   ViceFid     VFid;
+                   char        *name;          /* Place holder for data. */
+               } cfs_remove;
+
+
+
+     out
+
+       none
+
+  Description
+    Remove file named cfs_remove_in.name in directory
+    identified by   VFid.
+
+
+  .. Note::
+
+     The attributes of the directory should be returned since its
+     mtime and size may change.
+
+
+4.13.  rmdir
+------------
+
+
+  Summary
+    Remove a directory
+
+  Arguments
+     in::
+
+               struct cfs_rmdir_in {
+                   ViceFid     VFid;
+                   char        *name;          /* Place holder for data. */
+               } cfs_rmdir;
+
+
+
+     out
+
+       none
+
+  Description
+    Remove the directory with name name from the directory
+    identified by VFid.
+
+  .. Note:: The attributes of the parent directory should be returned since
+           its mtime and size may change.
+
+
+4.14.  readlink
+---------------
+
+
+  Summary
+    Read the value of a symbolic link.
+
+  Arguments
+     in::
+
+               struct cfs_readlink_in {
+                   ViceFid VFid;
+               } cfs_readlink;
+
+
+
+     out::
+
+               struct cfs_readlink_out {
+                   int count;
+                   caddr_t     data;           /* Place holder for data. */
+               } cfs_readlink;
+
+
+
+  Description
+    This routine reads the contents of symbolic link
+    identified by VFid into the buffer data.  The buffer data must be able
+    to hold any name up to CFS_MAXNAMLEN (PATH or NAM??).
+
+  Errors
+    No unusual errors.
+
+
+4.15.  open
+-----------
+
+
+  Summary
+    Open a file.
+
+  Arguments
+     in::
+
+               struct cfs_open_in {
+                   ViceFid     VFid;
+                   int flags;
+               } cfs_open;
+
+
+
+     out::
+
+               struct cfs_open_out {
+                   dev_t       dev;
+                   ino_t       inode;
+               } cfs_open;
+
+
+
+  Description
+    This request asks Venus to place the file identified by
+    VFid in its cache and to note that the calling process wishes to open
+    it with flags as in open(2).  The return value to the kernel differs
+    for Unix and Windows systems.  For Unix systems the Coda FS Driver is
+    informed of the device and inode number of the container file in the
+    fields dev and inode.  For Windows the path of the container file is
+    returned to the kernel.
+
+
+  .. Note::
+
+     Currently the cfs_open_out structure is not properly adapted to
+     deal with the Windows case.  It might be best to implement two
+     upcalls, one to open aiming at a container file name, the other at a
+     container file inode.
+
+
+4.16.  close
+------------
+
+
+  Summary
+    Close a file, update it on the servers.
+
+  Arguments
+     in::
+
+               struct cfs_close_in {
+                   ViceFid     VFid;
+                   int flags;
+               } cfs_close;
+
+
+
+     out
+
+       none
+
+  Description
+    Close the file identified by VFid.
+
+  .. Note::
+
+     The flags argument is bogus and not used.  However, Venus' code
+     has room to deal with an execp input field, probably this field should
+     be used to inform Venus that the file was closed but is still memory
+     mapped for execution.  There are comments about fetching versus not
+     fetching the data in Venus vproc_vfscalls.  This seems silly.  If a
+     file is being closed, the data in the container file is to be the new
+     data.  Here again the execp flag might be in play to create confusion:
+     currently Venus might think a file can be flushed from the cache when
+     it is still memory mapped.  This needs to be understood.
+
+
+4.17.  ioctl
+------------
+
+
+  Summary
+    Do an ioctl on a file. This includes the pioctl interface.
+
+  Arguments
+     in::
+
+               struct cfs_ioctl_in {
+                   ViceFid VFid;
+                   int cmd;
+                   int len;
+                   int rwflag;
+                   char *data;                 /* Place holder for data. */
+               } cfs_ioctl;
+
+
+
+     out::
+
+
+               struct cfs_ioctl_out {
+                   int len;
+                   caddr_t     data;           /* Place holder for data. */
+               } cfs_ioctl;
+
+
+
+  Description
+    Do an ioctl operation on a file.  The command, len and
+    data arguments are filled as usual.  flags is not used by Venus.
+
+  .. Note::
+
+     Another bogus parameter.  flags is not used.  What is the
+     business about PREFETCHING in the Venus code?
+
+
+
+4.18.  rename
+-------------
+
+
+  Summary
+    Rename a fid.
+
+  Arguments
+     in::
+
+               struct cfs_rename_in {
+                   ViceFid     sourceFid;
+                   char        *srcname;
+                   ViceFid destFid;
+                   char        *destname;
+               } cfs_rename;
+
+
+
+     out
+
+       none
+
+  Description
+    Rename the object with name srcname in directory
+    sourceFid to destname in destFid.   It is important that the names
+    srcname and destname are 0 terminated strings.  Strings in Unix
+    kernels are not always null terminated.
+
+
+4.19.  readdir
+--------------
+
+
+  Summary
+    Read directory entries.
+
+  Arguments
+     in::
+
+               struct cfs_readdir_in {
+                   ViceFid     VFid;
+                   int count;
+                   int offset;
+               } cfs_readdir;
+
+
+
+
+     out::
+
+               struct cfs_readdir_out {
+                   int size;
+                   caddr_t     data;           /* Place holder for data. */
+               } cfs_readdir;
+
+
+
+  Description
+    Read directory entries from VFid starting at offset and
+    read at most count bytes.  Returns the data in data and returns
+    the size in size.
+
+
+  .. Note::
+
+     This call is not used.  Readdir operations exploit container
+     files.  We will re-evaluate this during the directory revamp which is
+     about to take place.
+
+
+4.20.  vget
+-----------
+
+
+  Summary
+    instructs Venus to do an FSDB->Get.
+
+  Arguments
+     in::
+
+               struct cfs_vget_in {
+                   ViceFid VFid;
+               } cfs_vget;
+
+
+
+     out::
+
+               struct cfs_vget_out {
+                   ViceFid VFid;
+                   int vtype;
+               } cfs_vget;
+
+
+
+  Description
+    This upcall asks Venus to do a get operation on an fsobj
+    labelled by VFid.
+
+  .. Note::
+
+     This operation is not used.  However, it is extremely useful
+     since it can be used to deal with read/write memory mapped files.
+     These can be "pinned" in the Venus cache using vget and released with
+     inactive.
+
+
+4.21.  fsync
+------------
+
+
+  Summary
+    Tell Venus to update the RVM attributes of a file.
+
+  Arguments
+     in::
+
+               struct cfs_fsync_in {
+                   ViceFid VFid;
+               } cfs_fsync;
+
+
+
+     out
+
+       none
+
+  Description
+    Ask Venus to update RVM attributes of object VFid. This
+    should be called as part of kernel level fsync type calls.  The
+    result indicates if the syncing was successful.
+
+  .. Note:: Linux does not implement this call. It should.
+
+
+4.22.  inactive
+---------------
+
+
+  Summary
+    Tell Venus a vnode is no longer in use.
+
+  Arguments
+     in::
+
+               struct cfs_inactive_in {
+                   ViceFid VFid;
+               } cfs_inactive;
+
+
+
+     out
+
+       none
+
+  Description
+    This operation returns EOPNOTSUPP.
+
+  .. Note:: This should perhaps be removed.
+
+
+4.23.  rdwr
+-----------
+
+
+  Summary
+    Read or write from a file
+
+  Arguments
+     in::
+
+               struct cfs_rdwr_in {
+                   ViceFid     VFid;
+                   int rwflag;
+                   int count;
+                   int offset;
+                   int ioflag;
+                   caddr_t     data;           /* Place holder for data. */
+               } cfs_rdwr;
+
+
+
+
+     out::
+
+               struct cfs_rdwr_out {
+                   int rwflag;
+                   int count;
+                   caddr_t     data;   /* Place holder for data. */
+               } cfs_rdwr;
+
+
+
+  Description
+    This upcall asks Venus to read or write from a file.
+
+
+  .. Note::
+
+    It should be removed since it is against the Coda philosophy that
+    read/write operations never reach Venus.  I have been told the
+    operation does not work.  It is not currently used.
+
+
+
+4.24.  odymount
+---------------
+
+
+  Summary
+    Allows mounting multiple Coda "filesystems" on one Unix mount point.
+
+  Arguments
+     in::
+
+               struct ody_mount_in {
+                   char        *name;          /* Place holder for data. */
+               } ody_mount;
+
+
+
+     out::
+
+               struct ody_mount_out {
+                   ViceFid VFid;
+               } ody_mount;
+
+
+
+  Description
+    Asks Venus to return the rootfid of a Coda system named
+    name.  The fid is returned in VFid.
+
+  .. Note::
+
+     This call was used by David for dynamic sets.  It should be
+     removed since it causes a jungle of pointers in the VFS mounting area.
+     It is not used by Coda proper.  Call is not implemented by Venus.
+
+
+4.25.  ody_lookup
+-----------------
+
+
+  Summary
+    Looks up something.
+
+  Arguments
+     in
+
+       irrelevant
+
+
+     out
+
+       irrelevant
+
+
+  .. Note:: Gut it. Call is not implemented by Venus.
+
+
+4.26.  ody_expand
+-----------------
+
+
+  Summary
+    expands something in a dynamic set.
+
+  Arguments
+     in
+
+       irrelevant
+
+     out
+
+       irrelevant
+
+  .. Note:: Gut it. Call is not implemented by Venus.
+
+
+4.27.  prefetch
+---------------
+
+
+  Summary
+    Prefetch a dynamic set.
+
+  Arguments
+
+     in
+
+       Not documented.
+
+     out
+
+       Not documented.
+
+  Description
+    Venus worker.cc has support for this call, although it is
+    noted that it doesn't work.  Not surprising, since the kernel does not
+    have support for it. (ODY_PREFETCH is not a defined operation).
+
+
+  .. Note:: Gut it. It isn't working and isn't used by Coda.
+
+
+
+4.28.  signal
+-------------
+
+
+  Summary
+    Send Venus a signal about an upcall.
+
+  Arguments
+     in
+
+       none
+
+     out
+
+       not applicable.
+
+  Description
+    This is an out-of-band upcall to Venus to inform Venus
+    that the calling process received a signal after Venus read the
+    message from the input queue.  Venus is supposed to clean up the
+    operation.
+
+  Errors
+    No reply is given.
+
+  .. Note::
+
+     We need to better understand what Venus needs to clean up and if
+     it is doing this correctly.  Also we need to handle multiple upcall
+     per system call situations correctly.  It would be important to know
+     what state changes in Venus take place after an upcall for which the
+     kernel is responsible for notifying Venus to clean up (e.g. open
+     definitely is such a state change, but many others are maybe not).
+
+
+5.  The minicache and downcalls
+===============================
+
+
+  The Coda FS Driver can cache results of lookup and access upcalls, to
+  limit the frequency of upcalls.  Upcalls carry a price since a process
+  context switch needs to take place.  The counterpart of caching the
+  information is that Venus will notify the FS Driver that cached
+  entries must be flushed or renamed.
+
+  The kernel code generally has to maintain a structure which links the
+  internal file handles (called vnodes in BSD, inodes in Linux and
+  FileHandles in Windows) with the ViceFid's which Venus maintains.  The
+  reason is that frequent translations back and forth are needed in
+  order to make upcalls and use the results of upcalls.  Such linking
+  objects are called cnodes.
+
+  The current minicache implementations have cache entries which record
+  the following:
+
+  1. the name of the file
+
+  2. the cnode of the directory containing the object
+
+  3. a list of CodaCred's for which the lookup is permitted.
+
+  4. the cnode of the object
+
+  The lookup call in the Coda FS Driver may request the cnode of the
+  desired object from the cache, by passing its name, directory and the
+  CodaCred's of the caller.  The cache will return the cnode or indicate
+  that it cannot be found.  The Coda FS Driver must be careful to
+  invalidate cache entries when it modifies or removes objects.
+
+  When Venus obtains information that indicates that cache entries are
+  no longer valid, it will make a downcall to the kernel.  Downcalls are
+  intercepted by the Coda FS Driver and lead to cache invalidations of
+  the kind described below.  The Coda FS Driver does not return an error
+  unless the downcall data could not be read into kernel memory.
+
+
+5.1.  INVALIDATE
+----------------
+
+
+  No information is available on this call.
+
+
+5.2.  FLUSH
+-----------
+
+
+
+  Arguments
+    None
+
+  Summary
+    Flush the name cache entirely.
+
+  Description
+    Venus issues this call upon startup and when it dies. This
+    is to prevent stale cache information being held.  Some operating
+    systems allow the kernel name cache to be switched off dynamically.
+    When this is done, this downcall is made.
+
+
+5.3.  PURGEUSER
+---------------
+
+
+  Arguments
+    ::
+
+         struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */
+             struct CodaCred cred;
+         } cfs_purgeuser;
+
+
+
+  Description
+    Remove all entries in the cache carrying the Cred.  This
+    call is issued when tokens for a user expire or are flushed.
+
+
+5.4.  ZAPFILE
+-------------
+
+
+  Arguments
+    ::
+
+         struct cfs_zapfile_out {  /* CFS_ZAPFILE is a venus->kernel call */
+             ViceFid CodaFid;
+         } cfs_zapfile;
+
+
+
+  Description
+    Remove all entries which have the (dir vnode, name) pair.
+    This is issued as a result of an invalidation of cached attributes of
+    a vnode.
+
+  .. Note::
+
+     Call is not named correctly in NetBSD and Mach.  The minicache
+     zapfile routine takes different arguments. Linux does not implement
+     the invalidation of attributes correctly.
+
+
+
+5.5.  ZAPDIR
+------------
+
+
+  Arguments
+    ::
+
+         struct cfs_zapdir_out {   /* CFS_ZAPDIR is a venus->kernel call */
+             ViceFid CodaFid;
+         } cfs_zapdir;
+
+
+
+  Description
+    Remove all entries in the cache lying in a directory
+    CodaFid, and all children of this directory. This call is issued when
+    Venus receives a callback on the directory.
+
+
+5.6.  ZAPVNODE
+--------------
+
+
+
+  Arguments
+    ::
+
+         struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */
+             struct CodaCred cred;
+             ViceFid VFid;
+         } cfs_zapvnode;
+
+
+
+  Description
+    Remove all entries in the cache carrying the cred and VFid
+    as in the arguments. This downcall is probably never issued.
+
+
+5.7.  PURGEFID
+--------------
+
+
+  Arguments
+    ::
+
+         struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */
+             ViceFid CodaFid;
+         } cfs_purgefid;
+
+
+
+  Description
+    Flush the attribute for the file. If it is a dir (odd
+    vnode), purge its children from the namecache and remove the file from the
+    namecache.
+
+
+
+5.8.  REPLACE
+-------------
+
+
+  Summary
+    Replace the Fid's for a collection of names.
+
+  Arguments
+    ::
+
+         struct cfs_replace_out { /* cfs_replace is a venus->kernel call */
+             ViceFid NewFid;
+             ViceFid OldFid;
+         } cfs_replace;
+
+
+
+  Description
+    This routine replaces a ViceFid in the name cache with
+    another.  It is added to allow Venus during reintegration to replace
+    locally allocated temp fids while disconnected with global fids even
+    when the reference counts on those fids are not zero.
+
+
+6.  Initialization and cleanup
+==============================
+
+
+  This section gives brief hints as to desirable features for the Coda
+  FS Driver at startup and upon shutdown or Venus failures.  Before
+  entering the discussion it is useful to repeat that the Coda FS Driver
+  maintains the following data:
+
+
+  1. message queues
+
+  2. cnodes
+
+  3. name cache entries
+
+     The name cache entries are entirely private to the driver, so they
+     can easily be manipulated.   The message queues will generally have
+     clear points of initialization and destruction.  The cnodes are
+     much more delicate.  User processes hold reference counts in Coda
+     filesystems and it can be difficult to clean up the cnodes.
+
+  It can expect requests through:
+
+  1. the message subsystem
+
+  2. the VFS layer
+
+  3. pioctl interface
+
+     Currently the pioctl passes through the VFS for Coda so we can
+     treat these similarly.
+
+
+6.1.  Requirements
+------------------
+
+
+  The following requirements should be accommodated:
+
+  1. The message queues should have open and close routines.  On Unix
+     the opening of the character devices are such routines.
+
+    -  Before opening, no messages can be placed.
+
+    -  Opening will remove any old messages still pending.
+
+    -  Close will notify any sleeping processes that their upcall cannot
+       be completed.
+
+    -  Close will free all memory allocated by the message queues.
+
+
+  2. At open the namecache shall be initialized to empty state.
+
+  3. Before the message queues are open, all VFS operations will fail.
+     Fortunately this can be achieved by making sure than mounting the
+     Coda filesystem cannot succeed before opening.
+
+  4. After closing of the queues, no VFS operations can succeed.  Here
+     one needs to be careful, since a few operations (lookup,
+     read/write, readdir) can proceed without upcalls.  These must be
+     explicitly blocked.
+
+  5. Upon closing the namecache shall be flushed and disabled.
+
+  6. All memory held by cnodes can be freed without relying on upcalls.
+
+  7. Unmounting the file system can be done without relying on upcalls.
+
+  8. Mounting the Coda filesystem should fail gracefully if Venus cannot
+     get the rootfid or the attributes of the rootfid.  The latter is
+     best implemented by Venus fetching these objects before attempting
+     to mount.
+
+  .. Note::
+
+     NetBSD in particular but also Linux have not implemented the
+     above requirements fully.  For smooth operation this needs to be
+     corrected.
+
+
+
diff --git a/Documentation/filesystems/coda.txt b/Documentation/filesystems/coda.txt
deleted file mode 100644 (file)
index 1711ad4..0000000
+++ /dev/null
@@ -1,1676 +0,0 @@
-NOTE: 
-This is one of the technical documents describing a component of
-Coda -- this document describes the client kernel-Venus interface.
-
-For more information:
-  http://www.coda.cs.cmu.edu
-For user level software needed to run Coda:
-  ftp://ftp.coda.cs.cmu.edu
-
-To run Coda you need to get a user level cache manager for the client,
-named Venus, as well as tools to manipulate ACLs, to log in, etc.  The
-client needs to have the Coda filesystem selected in the kernel
-configuration.
-
-The server needs a user level server and at present does not depend on
-kernel support.
-
-
-
-
-
-
-
-  The Venus kernel interface
-  Peter J. Braam
-  v1.0, Nov 9, 1997
-
-  This document describes the communication between Venus and kernel
-  level filesystem code needed for the operation of the Coda file sys-
-  tem.  This document version is meant to describe the current interface
-  (version 1.0) as well as improvements we envisage.
-  ______________________________________________________________________
-
-  Table of Contents
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  1. Introduction
-
-  2. Servicing Coda filesystem calls
-
-  3. The message layer
-
-     3.1 Implementation details
-
-  4. The interface at the call level
-
-     4.1 Data structures shared by the kernel and Venus
-     4.2 The pioctl interface
-     4.3 root
-     4.4 lookup
-     4.5 getattr
-     4.6 setattr
-     4.7 access
-     4.8 create
-     4.9 mkdir
-     4.10 link
-     4.11 symlink
-     4.12 remove
-     4.13 rmdir
-     4.14 readlink
-     4.15 open
-     4.16 close
-     4.17 ioctl
-     4.18 rename
-     4.19 readdir
-     4.20 vget
-     4.21 fsync
-     4.22 inactive
-     4.23 rdwr
-     4.24 odymount
-     4.25 ody_lookup
-     4.26 ody_expand
-     4.27 prefetch
-     4.28 signal
-
-  5. The minicache and downcalls
-
-     5.1 INVALIDATE
-     5.2 FLUSH
-     5.3 PURGEUSER
-     5.4 ZAPFILE
-     5.5 ZAPDIR
-     5.6 ZAPVNODE
-     5.7 PURGEFID
-     5.8 REPLACE
-
-  6. Initialization and cleanup
-
-     6.1 Requirements
-
-
-  ______________________________________________________________________
-  0wpage
-
-  1\b1.\b.  I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
-
-
-
-  A key component in the Coda Distributed File System is the cache
-  manager, _\bV_\be_\bn_\bu_\bs.
-
-
-  When processes on a Coda enabled system access files in the Coda
-  filesystem, requests are directed at the filesystem layer in the
-  operating system. The operating system will communicate with Venus to
-  service the request for the process.  Venus manages a persistent
-  client cache and makes remote procedure calls to Coda file servers and
-  related servers (such as authentication servers) to service these
-  requests it receives from the operating system.  When Venus has
-  serviced a request it replies to the operating system with appropriate
-  return codes, and other data related to the request.  Optionally the
-  kernel support for Coda may maintain a minicache of recently processed
-  requests to limit the number of interactions with Venus.  Venus
-  possesses the facility to inform the kernel when elements from its
-  minicache are no longer valid.
-
-  This document describes precisely this communication between the
-  kernel and Venus.  The definitions of so called upcalls and downcalls
-  will be given with the format of the data they handle. We shall also
-  describe the semantic invariants resulting from the calls.
-
-  Historically Coda was implemented in a BSD file system in Mach 2.6.
-  The interface between the kernel and Venus is very similar to the BSD
-  VFS interface.  Similar functionality is provided, and the format of
-  the parameters and returned data is very similar to the BSD VFS.  This
-  leads to an almost natural environment for implementing a kernel-level
-  filesystem driver for Coda in a BSD system.  However, other operating
-  systems such as Linux and Windows 95 and NT have virtual filesystem
-  with different interfaces.
-
-  To implement Coda on these systems some reverse engineering of the
-  Venus/Kernel protocol is necessary.  Also it came to light that other
-  systems could profit significantly from certain small optimizations
-  and modifications to the protocol. To facilitate this work as well as
-  to make future ports easier, communication between Venus and the
-  kernel should be documented in great detail.  This is the aim of this
-  document.
-
-  0wpage
-
-  2\b2.\b.  S\bSe\ber\brv\bvi\bic\bci\bin\bng\bg C\bCo\bod\bda\ba f\bfi\bil\ble\bes\bsy\bys\bst\bte\bem\bm c\bca\bal\bll\bls\bs
-
-  The service of a request for a Coda file system service originates in
-  a process P\bP which accessing a Coda file. It makes a system call which
-  traps to the OS kernel. Examples of such calls trapping to the kernel
-  are _\br_\be_\ba_\bd_\b, _\bw_\br_\bi_\bt_\be_\b, _\bo_\bp_\be_\bn_\b, _\bc_\bl_\bo_\bs_\be_\b, _\bc_\br_\be_\ba_\bt_\be_\b, _\bm_\bk_\bd_\bi_\br_\b, _\br_\bm_\bd_\bi_\br_\b, _\bc_\bh_\bm_\bo_\bd in a Unix
-  context.  Similar calls exist in the Win32 environment, and are named
-  _\bC_\br_\be_\ba_\bt_\be_\bF_\bi_\bl_\be_\b, .
-
-  Generally the operating system handles the request in a virtual
-  filesystem (VFS) layer, which is named I/O Manager in NT and IFS
-  manager in Windows 95.  The VFS is responsible for partial processing
-  of the request and for locating the specific filesystem(s) which will
-  service parts of the request.  Usually the information in the path
-  assists in locating the correct FS drivers.  Sometimes after extensive
-  pre-processing, the VFS starts invoking exported routines in the FS
-  driver.  This is the point where the FS specific processing of the
-  request starts, and here the Coda specific kernel code comes into
-  play.
-
-  The FS layer for Coda must expose and implement several interfaces.
-  First and foremost the VFS must be able to make all necessary calls to
-  the Coda FS layer, so the Coda FS driver must expose the VFS interface
-  as applicable in the operating system. These differ very significantly
-  among operating systems, but share features such as facilities to
-  read/write and create and remove objects.  The Coda FS layer services
-  such VFS requests by invoking one or more well defined services
-  offered by the cache manager Venus.  When the replies from Venus have
-  come back to the FS driver, servicing of the VFS call continues and
-  finishes with a reply to the kernel's VFS. Finally the VFS layer
-  returns to the process.
-
-  As a result of this design a basic interface exposed by the FS driver
-  must allow Venus to manage message traffic.  In particular Venus must
-  be able to retrieve and place messages and to be notified of the
-  arrival of a new message. The notification must be through a mechanism
-  which does not block Venus since Venus must attend to other tasks even
-  when no messages are waiting or being processed.
-
-
-
-
-
-
-                     Interfaces of the Coda FS Driver
-
-  Furthermore the FS layer provides for a special path of communication
-  between a user process and Venus, called the pioctl interface. The
-  pioctl interface is used for Coda specific services, such as
-  requesting detailed information about the persistent cache managed by
-  Venus. Here the involvement of the kernel is minimal.  It identifies
-  the calling process and passes the information on to Venus.  When
-  Venus replies the response is passed back to the caller in unmodified
-  form.
-
-  Finally Venus allows the kernel FS driver to cache the results from
-  certain services.  This is done to avoid excessive context switches
-  and results in an efficient system.  However, Venus may acquire
-  information, for example from the network which implies that cached
-  information must be flushed or replaced. Venus then makes a downcall
-  to the Coda FS layer to request flushes or updates in the cache.  The
-  kernel FS driver handles such requests synchronously.
-
-  Among these interfaces the VFS interface and the facility to place,
-  receive and be notified of messages are platform specific.  We will
-  not go into the calls exported to the VFS layer but we will state the
-  requirements of the message exchange mechanism.
-
-  0wpage
-
-  3\b3.\b.  T\bTh\bhe\be m\bme\bes\bss\bsa\bag\bge\be l\bla\bay\bye\ber\br
-
-
-
-  At the lowest level the communication between Venus and the FS driver
-  proceeds through messages.  The synchronization between processes
-  requesting Coda file service and Venus relies on blocking and waking
-  up processes.  The Coda FS driver processes VFS- and pioctl-requests
-  on behalf of a process P, creates messages for Venus, awaits replies
-  and finally returns to the caller.  The implementation of the exchange
-  of messages is platform specific, but the semantics have (so far)
-  appeared to be generally applicable.  Data buffers are created by the
-  FS Driver in kernel memory on behalf of P and copied to user memory in
-  Venus.
-
-  The FS Driver while servicing P makes upcalls to Venus.  Such an
-  upcall is dispatched to Venus by creating a message structure.  The
-  structure contains the identification of P, the message sequence
-  number, the size of the request and a pointer to the data in kernel
-  memory for the request.  Since the data buffer is re-used to hold the
-  reply from Venus, there is a field for the size of the reply.  A flags
-  field is used in the message to precisely record the status of the
-  message.  Additional platform dependent structures involve pointers to
-  determine the position of the message on queues and pointers to
-  synchronization objects.  In the upcall routine the message structure
-  is filled in, flags are set to 0, and it is placed on the _\bp_\be_\bn_\bd_\bi_\bn_\bg
-  queue.  The routine calling upcall is responsible for allocating the
-  data buffer; its structure will be described in the next section.
-
-  A facility must exist to notify Venus that the message has been
-  created, and implemented using available synchronization objects in
-  the OS. This notification is done in the upcall context of the process
-  P. When the message is on the pending queue, process P cannot proceed
-  in upcall.  The (kernel mode) processing of P in the filesystem
-  request routine must be suspended until Venus has replied.  Therefore
-  the calling thread in P is blocked in upcall.  A pointer in the
-  message structure will locate the synchronization object on which P is
-  sleeping.
-
-  Venus detects the notification that a message has arrived, and the FS
-  driver allow Venus to retrieve the message with a getmsg_from_kernel
-  call. This action finishes in the kernel by putting the message on the
-  queue of processing messages and setting flags to READ.  Venus is
-  passed the contents of the data buffer. The getmsg_from_kernel call
-  now returns and Venus processes the request.
-
-  At some later point the FS driver receives a message from Venus,
-  namely when Venus calls sendmsg_to_kernel.  At this moment the Coda FS
-  driver looks at the contents of the message and decides if:
-
-
-  +\bo  the message is a reply for a suspended thread P.  If so it removes
-     the message from the processing queue and marks the message as
-     WRITTEN.  Finally, the FS driver unblocks P (still in the kernel
-     mode context of Venus) and the sendmsg_to_kernel call returns to
-     Venus.  The process P will be scheduled at some point and continues
-     processing its upcall with the data buffer replaced with the reply
-     from Venus.
-
-  +\bo  The message is a _\bd_\bo_\bw_\bn_\bc_\ba_\bl_\bl.  A downcall is a request from Venus to
-     the FS Driver. The FS driver processes the request immediately
-     (usually a cache eviction or replacement) and when it finishes
-     sendmsg_to_kernel returns.
-
-  Now P awakes and continues processing upcall.  There are some
-  subtleties to take account of. First P will determine if it was woken
-  up in upcall by a signal from some other source (for example an
-  attempt to terminate P) or as is normally the case by Venus in its
-  sendmsg_to_kernel call.  In the normal case, the upcall routine will
-  deallocate the message structure and return.  The FS routine can proceed
-  with its processing.
-
-
-
-
-
-
-
-                      Sleeping and IPC arrangements
-
-  In case P is woken up by a signal and not by Venus, it will first look
-  at the flags field.  If the message is not yet READ, the process P can
-  handle its signal without notifying Venus.  If Venus has READ, and
-  the request should not be processed, P can send Venus a signal message
-  to indicate that it should disregard the previous message.  Such
-  signals are put in the queue at the head, and read first by Venus.  If
-  the message is already marked as WRITTEN it is too late to stop the
-  processing.  The VFS routine will now continue.  (-- If a VFS request
-  involves more than one upcall, this can lead to complicated state, an
-  extra field "handle_signals" could be added in the message structure
-  to indicate points of no return have been passed.--)
-
-
-
-  3\b3.\b.1\b1.\b.  I\bIm\bmp\bpl\ble\bem\bme\ben\bnt\bta\bat\bti\bio\bon\bn d\bde\bet\bta\bai\bil\bls\bs
-
-  The Unix implementation of this mechanism has been through the
-  implementation of a character device associated with Coda.  Venus
-  retrieves messages by doing a read on the device, replies are sent
-  with a write and notification is through the select system call on the
-  file descriptor for the device.  The process P is kept waiting on an
-  interruptible wait queue object.
-
-  In Windows NT and the DPMI Windows 95 implementation a DeviceIoControl
-  call is used.  The DeviceIoControl call is designed to copy buffers
-  from user memory to kernel memory with OPCODES. The sendmsg_to_kernel
-  is issued as a synchronous call, while the getmsg_from_kernel call is
-  asynchronous.  Windows EventObjects are used for notification of
-  message arrival.  The process P is kept waiting on a KernelEvent
-  object in NT and a semaphore in Windows 95.
-
-  0wpage
-
-  4\b4.\b.  T\bTh\bhe\be i\bin\bnt\bte\ber\brf\bfa\bac\bce\be a\bat\bt t\bth\bhe\be c\bca\bal\bll\bl l\ble\bev\bve\bel\bl
-
-
-  This section describes the upcalls a Coda FS driver can make to Venus.
-  Each of these upcalls make use of two structures: inputArgs and
-  outputArgs.   In pseudo BNF form the structures take the following
-  form:
-
-
-  struct inputArgs {
-      u_long opcode;
-      u_long unique;     /* Keep multiple outstanding msgs distinct */
-      u_short pid;                 /* Common to all */
-      u_short pgid;                /* Common to all */
-      struct CodaCred cred;        /* Common to all */
-
-      <union "in" of call dependent parts of inputArgs>
-  };
-
-  struct outputArgs {
-      u_long opcode;
-      u_long unique;       /* Keep multiple outstanding msgs distinct */
-      u_long result;
-
-      <union "out" of call dependent parts of inputArgs>
-  };
-
-
-
-  Before going on let us elucidate the role of the various fields. The
-  inputArgs start with the opcode which defines the type of service
-  requested from Venus. There are approximately 30 upcalls at present
-  which we will discuss.   The unique field labels the inputArg with a
-  unique number which will identify the message uniquely.  A process and
-  process group id are passed.  Finally the credentials of the caller
-  are included.
-
-  Before delving into the specific calls we need to discuss a variety of
-  data structures shared by the kernel and Venus.
-
-
-
-
-  4\b4.\b.1\b1.\b.  D\bDa\bat\bta\ba s\bst\btr\bru\buc\bct\btu\bur\bre\bes\bs s\bsh\bha\bar\bre\bed\bd b\bby\by t\bth\bhe\be k\bke\ber\brn\bne\bel\bl a\ban\bnd\bd V\bVe\ben\bnu\bus\bs
-
-
-  The CodaCred structure defines a variety of user and group ids as
-  they are set for the calling process. The vuid_t and vgid_t are 32 bit
-  unsigned integers.  It also defines group membership in an array.  On
-  Unix the CodaCred has proven sufficient to implement good security
-  semantics for Coda but the structure may have to undergo modification
-  for the Windows environment when these mature.
-
-  struct CodaCred {
-      vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, effective, set, fs uid */
-      vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
-      vgid_t cr_groups[NGROUPS];        /* Group membership for caller */
-  };
-
-
-
-  N\bNO\bOT\bTE\bE It is questionable if we need CodaCreds in Venus. Finally Venus
-  doesn't know about groups, although it does create files with the
-  default uid/gid.  Perhaps the list of group membership is superfluous.
-
-
-  The next item is the fundamental identifier used to identify Coda
-  files, the ViceFid.  A fid of a file uniquely defines a file or
-  directory in the Coda filesystem within a _\bc_\be_\bl_\bl.   (-- A _\bc_\be_\bl_\bl is a
-  group of Coda servers acting under the aegis of a single system
-  control machine or SCM. See the Coda Administration manual for a
-  detailed description of the role of the SCM.--)
-
-
-  typedef struct ViceFid {
-      VolumeId Volume;
-      VnodeId Vnode;
-      Unique_t Unique;
-  } ViceFid;
-
-
-
-  Each of the constituent fields: VolumeId, VnodeId and Unique_t are
-  unsigned 32 bit integers.  We envisage that a further field will need
-  to be prefixed to identify the Coda cell; this will probably take the
-  form of a Ipv6 size IP address naming the Coda cell through DNS.
-
-  The next important structure shared between Venus and the kernel is
-  the attributes of the file.  The following structure is used to
-  exchange information.  It has room for future extensions such as
-  support for device files (currently not present in Coda).
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  struct coda_timespec {
-          int64_t         tv_sec;         /* seconds */
-          long            tv_nsec;        /* nanoseconds */
-  };
-
-  struct coda_vattr {
-          enum coda_vtype va_type;        /* vnode type (for create) */
-          u_short         va_mode;        /* files access mode and type */
-          short           va_nlink;       /* number of references to file */
-          vuid_t          va_uid;         /* owner user id */
-          vgid_t          va_gid;         /* owner group id */
-          long            va_fsid;        /* file system id (dev for now) */
-          long            va_fileid;      /* file id */
-          u_quad_t        va_size;        /* file size in bytes */
-          long            va_blocksize;   /* blocksize preferred for i/o */
-          struct coda_timespec va_atime;  /* time of last access */
-          struct coda_timespec va_mtime;  /* time of last modification */
-          struct coda_timespec va_ctime;  /* time file changed */
-          u_long          va_gen;         /* generation number of file */
-          u_long          va_flags;       /* flags defined for file */
-          dev_t           va_rdev;        /* device special file represents */
-          u_quad_t        va_bytes;       /* bytes of disk space held by file */
-          u_quad_t        va_filerev;     /* file modification number */
-          u_int           va_vaflags;     /* operations flags, see below */
-          long            va_spare;       /* remain quad aligned */
-  };
-
-
-
-
-  4\b4.\b.2\b2.\b.  T\bTh\bhe\be p\bpi\bio\boc\bct\btl\bl i\bin\bnt\bte\ber\brf\bfa\bac\bce\be
-
-
-  Coda specific requests can be made by application through the pioctl
-  interface. The pioctl is implemented as an ordinary ioctl on a
-  fictitious file /coda/.CONTROL.  The pioctl call opens this file, gets
-  a file handle and makes the ioctl call. Finally it closes the file.
-
-  The kernel involvement in this is limited to providing the facility to
-  open and close and pass the ioctl message _\ba_\bn_\bd to verify that a path in
-  the pioctl data buffers is a file in a Coda filesystem.
-
-  The kernel is handed a data packet of the form:
-
-      struct {
-          const char *path;
-          struct ViceIoctl vidata;
-          int follow;
-      } data;
-
-
-
-  where
-
-
-  struct ViceIoctl {
-          caddr_t in, out;        /* Data to be transferred in, or out */
-          short in_size;          /* Size of input buffer <= 2K */
-          short out_size;         /* Maximum size of output buffer, <= 2K */
-  };
-
-
-
-  The path must be a Coda file, otherwise the ioctl upcall will not be
-  made.
-
-  N\bNO\bOT\bTE\bE  The data structures and code are a mess.  We need to clean this
-  up.
-
-  We now proceed to document the individual calls:
-
-  0wpage
-
-  4\b4.\b.3\b3.\b.  r\bro\boo\bot\bt
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn empty
-
-     o\bou\but\bt
-
-                struct cfs_root_out {
-                    ViceFid VFid;
-                } cfs_root;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This call is made to Venus during the initialization of
-  the Coda filesystem. If the result is zero, the cfs_root structure
-  contains the ViceFid of the root of the Coda filesystem. If a non-zero
-  result is generated, its value is a platform dependent error code
-  indicating the difficulty Venus encountered in locating the root of
-  the Coda filesystem.
-
-  0wpage
-
-  4\b4.\b.4\b4.\b.  l\blo\boo\bok\bku\bup\bp
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Find the ViceFid and type of an object in a directory if it
-  exists.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct  cfs_lookup_in {
-                    ViceFid     VFid;
-                    char        *name;          /* Place holder for data. */
-                } cfs_lookup;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_lookup_out {
-                    ViceFid VFid;
-                    int vtype;
-                } cfs_lookup;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This call is made to determine the ViceFid and filetype of
-  a directory entry.  The directory entry requested carries name name
-  and Venus will search the directory identified by cfs_lookup_in.VFid.
-  The result may indicate that the name does not exist, or that
-  difficulty was encountered in finding it (e.g. due to disconnection).
-  If the result is zero, the field cfs_lookup_out.VFid contains the
-  targets ViceFid and cfs_lookup_out.vtype the coda_vtype giving the
-  type of object the name designates.
-
-  The name of the object is an 8 bit character string of maximum length
-  CFS_MAXNAMLEN, currently set to 256 (including a 0 terminator.)
-
-  It is extremely important to realize that Venus bitwise ors the field
-  cfs_lookup.vtype with CFS_NOCACHE to indicate that the object should
-  not be put in the kernel name cache.
-
-  N\bNO\bOT\bTE\bE The type of the vtype is currently wrong.  It should be
-  coda_vtype. Linux does not take note of CFS_NOCACHE.  It should.
-
-  0wpage
-
-  4\b4.\b.5\b5.\b.  g\bge\bet\bta\bat\btt\btr\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Get the attributes of a file.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_getattr_in {
-                    ViceFid VFid;
-                    struct coda_vattr attr; /* XXXXX */
-                } cfs_getattr;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_getattr_out {
-                    struct coda_vattr attr;
-                } cfs_getattr;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This call returns the attributes of the file identified by
-  fid.
-
-  E\bEr\brr\bro\bor\brs\bs Errors can occur if the object with fid does not exist, is
-  unaccessible or if the caller does not have permission to fetch
-  attributes.
-
-  N\bNo\bot\bte\be Many kernel FS drivers (Linux, NT and Windows 95) need to acquire
-  the attributes as well as the Fid for the instantiation of an internal
-  "inode" or "FileHandle".  A significant improvement in performance on
-  such systems could be made by combining the _\bl_\bo_\bo_\bk_\bu_\bp and _\bg_\be_\bt_\ba_\bt_\bt_\br calls
-  both at the Venus/kernel interaction level and at the RPC level.
-
-  The vattr structure included in the input arguments is superfluous and
-  should be removed.
-
-  0wpage
-
-  4\b4.\b.6\b6.\b.  s\bse\bet\bta\bat\btt\btr\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Set the attributes of a file.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_setattr_in {
-                    ViceFid VFid;
-                    struct coda_vattr attr;
-                } cfs_setattr;
-
-
-
-
-     o\bou\but\bt
-        empty
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn The structure attr is filled with attributes to be changed
-  in BSD style.  Attributes not to be changed are set to -1, apart from
-  vtype which is set to VNON. Other are set to the value to be assigned.
-  The only attributes which the FS driver may request to change are the
-  mode, owner, groupid, atime, mtime and ctime.  The return value
-  indicates success or failure.
-
-  E\bEr\brr\bro\bor\brs\bs A variety of errors can occur.  The object may not exist, may
-  be inaccessible, or permission may not be granted by Venus.
-
-  0wpage
-
-  4\b4.\b.7\b7.\b.  a\bac\bcc\bce\bes\bss\bs
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_access_in {
-                    ViceFid     VFid;
-                    int flags;
-                } cfs_access;
-
-
-
-     o\bou\but\bt
-        empty
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Verify if access to the object identified by VFid for
-  operations described by flags is permitted.  The result indicates if
-  access will be granted.  It is important to remember that Coda uses
-  ACLs to enforce protection and that ultimately the servers, not the
-  clients enforce the security of the system.  The result of this call
-  will depend on whether a _\bt_\bo_\bk_\be_\bn is held by the user.
-
-  E\bEr\brr\bro\bor\brs\bs The object may not exist, or the ACL describing the protection
-  may not be accessible.
-
-  0wpage
-
-  4\b4.\b.8\b8.\b.  c\bcr\bre\bea\bat\bte\be
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Invoked to create a file
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_create_in {
-                    ViceFid VFid;
-                    struct coda_vattr attr;
-                    int excl;
-                    int mode;
-                    char        *name;          /* Place holder for data. */
-                } cfs_create;
-
-
-
-
-     o\bou\but\bt
-
-                struct cfs_create_out {
-                    ViceFid VFid;
-                    struct coda_vattr attr;
-                } cfs_create;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  This upcall is invoked to request creation of a file.
-  The file will be created in the directory identified by VFid, its name
-  will be name, and the mode will be mode.  If excl is set an error will
-  be returned if the file already exists.  If the size field in attr is
-  set to zero the file will be truncated.  The uid and gid of the file
-  are set by converting the CodaCred to a uid using a macro CRTOUID
-  (this macro is platform dependent).  Upon success the VFid and
-  attributes of the file are returned.  The Coda FS Driver will normally
-  instantiate a vnode, inode or file handle at kernel level for the new
-  object.
-
-
-  E\bEr\brr\bro\bor\brs\bs A variety of errors can occur. Permissions may be insufficient.
-  If the object exists and is not a file the error EISDIR is returned
-  under Unix.
-
-  N\bNO\bOT\bTE\bE The packing of parameters is very inefficient and appears to
-  indicate confusion between the system call creat and the VFS operation
-  create. The VFS operation create is only called to create new objects.
-  This create call differs from the Unix one in that it is not invoked
-  to return a file descriptor. The truncate and exclusive options,
-  together with the mode, could simply be part of the mode as it is
-  under Unix.  There should be no flags argument; this is used in open
-  (2) to return a file descriptor for READ or WRITE mode.
-
-  The attributes of the directory should be returned too, since the size
-  and mtime changed.
-
-  0wpage
-
-  4\b4.\b.9\b9.\b.  m\bmk\bkd\bdi\bir\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Create a new directory.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_mkdir_in {
-                    ViceFid     VFid;
-                    struct coda_vattr attr;
-                    char        *name;          /* Place holder for data. */
-                } cfs_mkdir;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_mkdir_out {
-                    ViceFid VFid;
-                    struct coda_vattr attr;
-                } cfs_mkdir;
-
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This call is similar to create but creates a directory.
-  Only the mode field in the input parameters is used for creation.
-  Upon successful creation, the attr returned contains the attributes of
-  the new directory.
-
-  E\bEr\brr\bro\bor\brs\bs As for create.
-
-  N\bNO\bOT\bTE\bE The input parameter should be changed to mode instead of
-  attributes.
-
-  The attributes of the parent should be returned since the size and
-  mtime changes.
-
-  0wpage
-
-  4\b4.\b.1\b10\b0.\b.  l\bli\bin\bnk\bk
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Create a link to an existing file.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_link_in {
-                    ViceFid sourceFid;          /* cnode to link *to* */
-                    ViceFid destFid;            /* Directory in which to place link */
-                    char        *tname;         /* Place holder for data. */
-                } cfs_link;
-
-
-
-     o\bou\but\bt
-        empty
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This call creates a link to the sourceFid in the directory
-  identified by destFid with name tname.  The source must reside in the
-  target's parent, i.e. the source must be have parent destFid, i.e. Coda
-  does not support cross directory hard links.  Only the return value is
-  relevant.  It indicates success or the type of failure.
-
-  E\bEr\brr\bro\bor\brs\bs The usual errors can occur.0wpage
-
-  4\b4.\b.1\b11\b1.\b.  s\bsy\bym\bml\bli\bin\bnk\bk
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by create a symbolic link
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_symlink_in {
-                    ViceFid     VFid;          /* Directory to put symlink in */
-                    char        *srcname;
-                    struct coda_vattr attr;
-                    char        *tname;
-                } cfs_symlink;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Create a symbolic link. The link is to be placed in the
-  directory identified by VFid and named tname.  It should point to the
-  pathname srcname.  The attributes of the newly created object are to
-  be set to attr.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE The attributes of the target directory should be returned since
-  its size changed.
-
-  0wpage
-
-  4\b4.\b.1\b12\b2.\b.  r\bre\bem\bmo\bov\bve\be
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Remove a file
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_remove_in {
-                    ViceFid     VFid;
-                    char        *name;          /* Place holder for data. */
-                } cfs_remove;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  Remove file named cfs_remove_in.name in directory
-  identified by   VFid.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE The attributes of the directory should be returned since its
-  mtime and size may change.
-
-  0wpage
-
-  4\b4.\b.1\b13\b3.\b.  r\brm\bmd\bdi\bir\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Remove a directory
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_rmdir_in {
-                    ViceFid     VFid;
-                    char        *name;          /* Place holder for data. */
-                } cfs_rmdir;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Remove the directory with name name from the directory
-  identified by VFid.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE The attributes of the parent directory should be returned since
-  its mtime and size may change.
-
-  0wpage
-
-  4\b4.\b.1\b14\b4.\b.  r\bre\bea\bad\bdl\bli\bin\bnk\bk
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Read the value of a symbolic link.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_readlink_in {
-                    ViceFid VFid;
-                } cfs_readlink;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_readlink_out {
-                    int count;
-                    caddr_t     data;           /* Place holder for data. */
-                } cfs_readlink;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This routine reads the contents of symbolic link
-  identified by VFid into the buffer data.  The buffer data must be able
-  to hold any name up to CFS_MAXNAMLEN (PATH or NAM??).
-
-  E\bEr\brr\bro\bor\brs\bs No unusual errors.
-
-  0wpage
-
-  4\b4.\b.1\b15\b5.\b.  o\bop\bpe\ben\bn
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Open a file.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_open_in {
-                    ViceFid     VFid;
-                    int flags;
-                } cfs_open;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_open_out {
-                    dev_t       dev;
-                    ino_t       inode;
-                } cfs_open;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  This request asks Venus to place the file identified by
-  VFid in its cache and to note that the calling process wishes to open
-  it with flags as in open(2).  The return value to the kernel differs
-  for Unix and Windows systems.  For Unix systems the Coda FS Driver is
-  informed of the device and inode number of the container file in the
-  fields dev and inode.  For Windows the path of the container file is
-  returned to the kernel.
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Currently the cfs_open_out structure is not properly adapted to
-  deal with the Windows case.  It might be best to implement two
-  upcalls, one to open aiming at a container file name, the other at a
-  container file inode.
-
-  0wpage
-
-  4\b4.\b.1\b16\b6.\b.  c\bcl\blo\bos\bse\be
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Close a file, update it on the servers.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_close_in {
-                    ViceFid     VFid;
-                    int flags;
-                } cfs_close;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Close the file identified by VFid.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE The flags argument is bogus and not used.  However, Venus' code
-  has room to deal with an execp input field, probably this field should
-  be used to inform Venus that the file was closed but is still memory
-  mapped for execution.  There are comments about fetching versus not
-  fetching the data in Venus vproc_vfscalls.  This seems silly.  If a
-  file is being closed, the data in the container file is to be the new
-  data.  Here again the execp flag might be in play to create confusion:
-  currently Venus might think a file can be flushed from the cache when
-  it is still memory mapped.  This needs to be understood.
-
-  0wpage
-
-  4\b4.\b.1\b17\b7.\b.  i\bio\boc\bct\btl\bl
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Do an ioctl on a file. This includes the pioctl interface.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_ioctl_in {
-                    ViceFid VFid;
-                    int cmd;
-                    int len;
-                    int rwflag;
-                    char *data;                 /* Place holder for data. */
-                } cfs_ioctl;
-
-
-
-     o\bou\but\bt
-
-
-                struct cfs_ioctl_out {
-                    int len;
-                    caddr_t     data;           /* Place holder for data. */
-                } cfs_ioctl;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Do an ioctl operation on a file.  The command, len and
-  data arguments are filled as usual.  flags is not used by Venus.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Another bogus parameter.  flags is not used.  What is the
-  business about PREFETCHING in the Venus code?
-
-
-  0wpage
-
-  4\b4.\b.1\b18\b8.\b.  r\bre\ben\bna\bam\bme\be
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Rename a fid.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_rename_in {
-                    ViceFid     sourceFid;
-                    char        *srcname;
-                    ViceFid destFid;
-                    char        *destname;
-                } cfs_rename;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  Rename the object with name srcname in directory
-  sourceFid to destname in destFid.   It is important that the names
-  srcname and destname are 0 terminated strings.  Strings in Unix
-  kernels are not always null terminated.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  0wpage
-
-  4\b4.\b.1\b19\b9.\b.  r\bre\bea\bad\bdd\bdi\bir\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Read directory entries.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_readdir_in {
-                    ViceFid     VFid;
-                    int count;
-                    int offset;
-                } cfs_readdir;
-
-
-
-
-     o\bou\but\bt
-
-                struct cfs_readdir_out {
-                    int size;
-                    caddr_t     data;           /* Place holder for data. */
-                } cfs_readdir;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Read directory entries from VFid starting at offset and
-  read at most count bytes.  Returns the data in data and returns
-  the size in size.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE This call is not used.  Readdir operations exploit container
-  files.  We will re-evaluate this during the directory revamp which is
-  about to take place.
-
-  0wpage
-
-  4\b4.\b.2\b20\b0.\b.  v\bvg\bge\bet\bt
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by instructs Venus to do an FSDB->Get.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_vget_in {
-                    ViceFid VFid;
-                } cfs_vget;
-
-
-
-     o\bou\but\bt
-
-                struct cfs_vget_out {
-                    ViceFid VFid;
-                    int vtype;
-                } cfs_vget;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This upcall asks Venus to do a get operation on an fsobj
-  labelled by VFid.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE This operation is not used.  However, it is extremely useful
-  since it can be used to deal with read/write memory mapped files.
-  These can be "pinned" in the Venus cache using vget and released with
-  inactive.
-
-  0wpage
-
-  4\b4.\b.2\b21\b1.\b.  f\bfs\bsy\byn\bnc\bc
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Tell Venus to update the RVM attributes of a file.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_fsync_in {
-                    ViceFid VFid;
-                } cfs_fsync;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Ask Venus to update RVM attributes of object VFid. This
-  should be called as part of kernel level fsync type calls.  The
-  result indicates if the syncing was successful.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Linux does not implement this call. It should.
-
-  0wpage
-
-  4\b4.\b.2\b22\b2.\b.  i\bin\bna\bac\bct\bti\biv\bve\be
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Tell Venus a vnode is no longer in use.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_inactive_in {
-                    ViceFid VFid;
-                } cfs_inactive;
-
-
-
-     o\bou\but\bt
-        none
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This operation returns EOPNOTSUPP.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE This should perhaps be removed.
-
-  0wpage
-
-  4\b4.\b.2\b23\b3.\b.  r\brd\bdw\bwr\br
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Read or write from a file
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct cfs_rdwr_in {
-                    ViceFid     VFid;
-                    int rwflag;
-                    int count;
-                    int offset;
-                    int ioflag;
-                    caddr_t     data;           /* Place holder for data. */
-                } cfs_rdwr;
-
-
-
-
-     o\bou\but\bt
-
-                struct cfs_rdwr_out {
-                    int rwflag;
-                    int count;
-                    caddr_t     data;   /* Place holder for data. */
-                } cfs_rdwr;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This upcall asks Venus to read or write from a file.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE It should be removed since it is against the Coda philosophy that
-  read/write operations never reach Venus.  I have been told the
-  operation does not work.  It is not currently used.
-
-
-  0wpage
-
-  4\b4.\b.2\b24\b4.\b.  o\bod\bdy\bym\bmo\bou\bun\bnt\bt
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Allows mounting multiple Coda "filesystems" on one Unix mount
-  point.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn
-
-                struct ody_mount_in {
-                    char        *name;          /* Place holder for data. */
-                } ody_mount;
-
-
-
-     o\bou\but\bt
-
-                struct ody_mount_out {
-                    ViceFid VFid;
-                } ody_mount;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  Asks Venus to return the rootfid of a Coda system named
-  name.  The fid is returned in VFid.
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE This call was used by David for dynamic sets.  It should be
-  removed since it causes a jungle of pointers in the VFS mounting area.
-  It is not used by Coda proper.  Call is not implemented by Venus.
-
-  0wpage
-
-  4\b4.\b.2\b25\b5.\b.  o\bod\bdy\by_\b_l\blo\boo\bok\bku\bup\bp
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Looks up something.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn irrelevant
-
-
-     o\bou\but\bt
-        irrelevant
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Gut it. Call is not implemented by Venus.
-
-  0wpage
-
-  4\b4.\b.2\b26\b6.\b.  o\bod\bdy\by_\b_e\bex\bxp\bpa\ban\bnd\bd
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by expands something in a dynamic set.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn irrelevant
-
-     o\bou\but\bt
-        irrelevant
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Gut it.  Call is not implemented by Venus.
-
-  0wpage
-
-  4\b4.\b.2\b27\b7.\b.  p\bpr\bre\bef\bfe\bet\btc\bch\bh
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Prefetch a dynamic set.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn Not documented.
-
-     o\bou\but\bt
-        Not documented.
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  Venus worker.cc has support for this call, although it is
-  noted that it doesn't work.  Not surprising, since the kernel does not
-  have support for it. (ODY_PREFETCH is not a defined operation).
-
-  E\bEr\brr\bro\bor\brs\bs
-
-  N\bNO\bOT\bTE\bE Gut it. It isn't working and isn't used by Coda.
-
-
-  0wpage
-
-  4\b4.\b.2\b28\b8.\b.  s\bsi\big\bgn\bna\bal\bl
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Send Venus a signal about an upcall.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-     i\bin\bn none
-
-     o\bou\but\bt
-        not applicable.
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn  This is an out-of-band upcall to Venus to inform Venus
-  that the calling process received a signal after Venus read the
-  message from the input queue.  Venus is supposed to clean up the
-  operation.
-
-  E\bEr\brr\bro\bor\brs\bs No reply is given.
-
-  N\bNO\bOT\bTE\bE We need to better understand what Venus needs to clean up and if
-  it is doing this correctly.  Also we need to handle multiple upcall
-  per system call situations correctly.  It would be important to know
-  what state changes in Venus take place after an upcall for which the
-  kernel is responsible for notifying Venus to clean up (e.g. open
-  definitely is such a state change, but many others are maybe not).
-
-  0wpage
-
-  5\b5.\b.  T\bTh\bhe\be m\bmi\bin\bni\bic\bca\bac\bch\bhe\be a\ban\bnd\bd d\bdo\bow\bwn\bnc\bca\bal\bll\bls\bs
-
-
-  The Coda FS Driver can cache results of lookup and access upcalls, to
-  limit the frequency of upcalls.  Upcalls carry a price since a process
-  context switch needs to take place.  The counterpart of caching the
-  information is that Venus will notify the FS Driver that cached
-  entries must be flushed or renamed.
-
-  The kernel code generally has to maintain a structure which links the
-  internal file handles (called vnodes in BSD, inodes in Linux and
-  FileHandles in Windows) with the ViceFid's which Venus maintains.  The
-  reason is that frequent translations back and forth are needed in
-  order to make upcalls and use the results of upcalls.  Such linking
-  objects are called c\bcn\bno\bod\bde\bes\bs.
-
-  The current minicache implementations have cache entries which record
-  the following:
-
-  1. the name of the file
-
-  2. the cnode of the directory containing the object
-
-  3. a list of CodaCred's for which the lookup is permitted.
-
-  4. the cnode of the object
-
-  The lookup call in the Coda FS Driver may request the cnode of the
-  desired object from the cache, by passing its name, directory and the
-  CodaCred's of the caller.  The cache will return the cnode or indicate
-  that it cannot be found.  The Coda FS Driver must be careful to
-  invalidate cache entries when it modifies or removes objects.
-
-  When Venus obtains information that indicates that cache entries are
-  no longer valid, it will make a downcall to the kernel.  Downcalls are
-  intercepted by the Coda FS Driver and lead to cache invalidations of
-  the kind described below.  The Coda FS Driver does not return an error
-  unless the downcall data could not be read into kernel memory.
-
-
-  5\b5.\b.1\b1.\b.  I\bIN\bNV\bVA\bAL\bLI\bID\bDA\bAT\bTE\bE
-
-
-  No information is available on this call.
-
-
-  5\b5.\b.2\b2.\b.  F\bFL\bLU\bUS\bSH\bH
-
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs None
-
-  S\bSu\bum\bmm\bma\bar\bry\by Flush the name cache entirely.
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Venus issues this call upon startup and when it dies. This
-  is to prevent stale cache information being held.  Some operating
-  systems allow the kernel name cache to be switched off dynamically.
-  When this is done, this downcall is made.
-
-
-  5\b5.\b.3\b3.\b.  P\bPU\bUR\bRG\bGE\bEU\bUS\bSE\bER\bR
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */
-              struct CodaCred cred;
-          } cfs_purgeuser;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Remove all entries in the cache carrying the Cred.  This
-  call is issued when tokens for a user expire or are flushed.
-
-
-  5\b5.\b.4\b4.\b.  Z\bZA\bAP\bPF\bFI\bIL\bLE\bE
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_zapfile_out {  /* CFS_ZAPFILE is a venus->kernel call */
-              ViceFid CodaFid;
-          } cfs_zapfile;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Remove all entries which have the (dir vnode, name) pair.
-  This is issued as a result of an invalidation of cached attributes of
-  a vnode.
-
-  N\bNO\bOT\bTE\bE Call is not named correctly in NetBSD and Mach.  The minicache
-  zapfile routine takes different arguments. Linux does not implement
-  the invalidation of attributes correctly.
-
-
-
-  5\b5.\b.5\b5.\b.  Z\bZA\bAP\bPD\bDI\bIR\bR
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_zapdir_out {   /* CFS_ZAPDIR is a venus->kernel call */
-              ViceFid CodaFid;
-          } cfs_zapdir;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Remove all entries in the cache lying in a directory
-  CodaFid, and all children of this directory. This call is issued when
-  Venus receives a callback on the directory.
-
-
-  5\b5.\b.6\b6.\b.  Z\bZA\bAP\bPV\bVN\bNO\bOD\bDE\bE
-
-
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */
-              struct CodaCred cred;
-              ViceFid VFid;
-          } cfs_zapvnode;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Remove all entries in the cache carrying the cred and VFid
-  as in the arguments. This downcall is probably never issued.
-
-
-  5\b5.\b.7\b7.\b.  P\bPU\bUR\bRG\bGE\bEF\bFI\bID\bD
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */
-              ViceFid CodaFid;
-          } cfs_purgefid;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn Flush the attribute for the file. If it is a dir (odd
-  vnode), purge its children from the namecache and remove the file from the
-  namecache.
-
-
-
-  5\b5.\b.8\b8.\b.  R\bRE\bEP\bPL\bLA\bAC\bCE\bE
-
-
-  S\bSu\bum\bmm\bma\bar\bry\by Replace the Fid's for a collection of names.
-
-  A\bAr\brg\bgu\bum\bme\ben\bnt\bts\bs
-
-          struct cfs_replace_out { /* cfs_replace is a venus->kernel call */
-              ViceFid NewFid;
-              ViceFid OldFid;
-          } cfs_replace;
-
-
-
-  D\bDe\bes\bsc\bcr\bri\bip\bpt\bti\bio\bon\bn This routine replaces a ViceFid in the name cache with
-  another.  It is added to allow Venus during reintegration to replace
-  locally allocated temp fids while disconnected with global fids even
-  when the reference counts on those fids are not zero.
-
-  0wpage
-
-  6\b6.\b.  I\bIn\bni\bit\bti\bia\bal\bli\biz\bza\bat\bti\bio\bon\bn a\ban\bnd\bd c\bcl\ble\bea\ban\bnu\bup\bp
-
-
-  This section gives brief hints as to desirable features for the Coda
-  FS Driver at startup and upon shutdown or Venus failures.  Before
-  entering the discussion it is useful to repeat that the Coda FS Driver
-  maintains the following data:
-
-
-  1. message queues
-
-  2. cnodes
-
-  3. name cache entries
-
-     The name cache entries are entirely private to the driver, so they
-     can easily be manipulated.   The message queues will generally have
-     clear points of initialization and destruction.  The cnodes are
-     much more delicate.  User processes hold reference counts in Coda
-     filesystems and it can be difficult to clean up the cnodes.
-
-  It can expect requests through:
-
-  1. the message subsystem
-
-  2. the VFS layer
-
-  3. pioctl interface
-
-     Currently the _\bp_\bi_\bo_\bc_\bt_\bl passes through the VFS for Coda so we can
-     treat these similarly.
-
-
-  6\b6.\b.1\b1.\b.  R\bRe\beq\bqu\bui\bir\bre\bem\bme\ben\bnt\bts\bs
-
-
-  The following requirements should be accommodated:
-
-  1. The message queues should have open and close routines.  On Unix
-     the opening of the character devices are such routines.
-
-  +\bo  Before opening, no messages can be placed.
-
-  +\bo  Opening will remove any old messages still pending.
-
-  +\bo  Close will notify any sleeping processes that their upcall cannot
-     be completed.
-
-  +\bo  Close will free all memory allocated by the message queues.
-
-
-  2. At open the namecache shall be initialized to empty state.
-
-  3. Before the message queues are open, all VFS operations will fail.
-     Fortunately this can be achieved by making sure than mounting the
-     Coda filesystem cannot succeed before opening.
-
-  4. After closing of the queues, no VFS operations can succeed.  Here
-     one needs to be careful, since a few operations (lookup,
-     read/write, readdir) can proceed without upcalls.  These must be
-     explicitly blocked.
-
-  5. Upon closing the namecache shall be flushed and disabled.
-
-  6. All memory held by cnodes can be freed without relying on upcalls.
-
-  7. Unmounting the file system can be done without relying on upcalls.
-
-  8. Mounting the Coda filesystem should fail gracefully if Venus cannot
-     get the rootfid or the attributes of the rootfid.  The latter is
-     best implemented by Venus fetching these objects before attempting
-     to mount.
-
-  N\bNO\bOT\bTE\bE  NetBSD in particular but also Linux have not implemented the
-  above requirements fully.  For smooth operation this needs to be
-  corrected.
-
-
-
similarity index 87%
rename from Documentation/filesystems/configfs/configfs.txt
rename to Documentation/filesystems/configfs.rst
index 16e606c11f405de5a24ad798b2677e2793e4da7d..f8941954c66714135ec8f3a3c465888f07e2e5b8 100644 (file)
@@ -1,5 +1,6 @@
-
-configfs - Userspace-driven kernel object configuration.
+=======================================================
+Configfs - Userspace-driven Kernel Object Configuration
+=======================================================
 
 Joel Becker <joel.becker@oracle.com>
 
@@ -9,7 +10,8 @@ Copyright (c) 2005 Oracle Corporation,
        Joel Becker <joel.becker@oracle.com>
 
 
-[What is configfs?]
+What is configfs?
+=================
 
 configfs is a ram-based filesystem that provides the converse of
 sysfs's functionality.  Where sysfs is a filesystem-based view of
@@ -35,10 +37,11 @@ kernel modules backing the items must respond to this.
 Both sysfs and configfs can and should exist together on the same
 system.  One is not a replacement for the other.
 
-[Using configfs]
+Using configfs
+==============
 
 configfs can be compiled as a module or into the kernel.  You can access
-it by doing
+it by doing::
 
        mount -t configfs none /config
 
@@ -56,28 +59,29 @@ values.  Don't mix more than one attribute in one attribute file.
 There are two types of configfs attributes:
 
 * Normal attributes, which similar to sysfs attributes, are small ASCII text
-files, with a maximum size of one page (PAGE_SIZE, 4096 on i386).  Preferably
-only one value per file should be used, and the same caveats from sysfs apply.
-Configfs expects write(2) to store the entire buffer at once.  When writing to
-normal configfs attributes, userspace processes should first read the entire
-file, modify the portions they wish to change, and then write the entire
-buffer back.
+  files, with a maximum size of one page (PAGE_SIZE, 4096 on i386).  Preferably
+  only one value per file should be used, and the same caveats from sysfs apply.
+  Configfs expects write(2) to store the entire buffer at once.  When writing to
+  normal configfs attributes, userspace processes should first read the entire
+  file, modify the portions they wish to change, and then write the entire
+  buffer back.
 
 * Binary attributes, which are somewhat similar to sysfs binary attributes,
-but with a few slight changes to semantics.  The PAGE_SIZE limitation does not
-apply, but the whole binary item must fit in single kernel vmalloc'ed buffer.
-The write(2) calls from user space are buffered, and the attributes'
-write_bin_attribute method will be invoked on the final close, therefore it is
-imperative for user-space to check the return code of close(2) in order to
-verify that the operation finished successfully.
-To avoid a malicious user OOMing the kernel, there's a per-binary attribute
-maximum buffer value.
+  but with a few slight changes to semantics.  The PAGE_SIZE limitation does not
+  apply, but the whole binary item must fit in single kernel vmalloc'ed buffer.
+  The write(2) calls from user space are buffered, and the attributes'
+  write_bin_attribute method will be invoked on the final close, therefore it is
+  imperative for user-space to check the return code of close(2) in order to
+  verify that the operation finished successfully.
+  To avoid a malicious user OOMing the kernel, there's a per-binary attribute
+  maximum buffer value.
 
 When an item needs to be destroyed, remove it with rmdir(2).  An
 item cannot be destroyed if any other item has a link to it (via
 symlink(2)).  Links can be removed via unlink(2).
 
-[Configuring FakeNBD: an Example]
+Configuring FakeNBD: an Example
+===============================
 
 Imagine there's a Network Block Device (NBD) driver that allows you to
 access remote block devices.  Call it FakeNBD.  FakeNBD uses configfs
@@ -86,14 +90,14 @@ sysadmins use to configure FakeNBD, but somehow that program has to tell
 the driver about it.  Here's where configfs comes in.
 
 When the FakeNBD driver is loaded, it registers itself with configfs.
-readdir(3) sees this just fine:
+readdir(3) sees this just fine::
 
        # ls /config
        fakenbd
 
 A fakenbd connection can be created with mkdir(2).  The name is
 arbitrary, but likely the tool will make some use of the name.  Perhaps
-it is a uuid or a disk name:
+it is a uuid or a disk name::
 
        # mkdir /config/fakenbd/disk1
        # ls /config/fakenbd/disk1
@@ -102,7 +106,7 @@ it is a uuid or a disk name:
 The target attribute contains the IP address of the server FakeNBD will
 connect to.  The device attribute is the device on the server.
 Predictably, the rw attribute determines whether the connection is
-read-only or read-write.
+read-only or read-write::
 
        # echo 10.0.0.1 > /config/fakenbd/disk1/target
        # echo /dev/sda1 > /config/fakenbd/disk1/device
@@ -111,7 +115,8 @@ read-only or read-write.
 That's it.  That's all there is.  Now the device is configured, via the
 shell no less.
 
-[Coding With configfs]
+Coding With configfs
+====================
 
 Every object in configfs is a config_item.  A config_item reflects an
 object in the subsystem.  It has attributes that match values on that
@@ -130,7 +135,10 @@ appears as a directory at the top of the configfs filesystem.  A
 subsystem is also a config_group, and can do everything a config_group
 can.
 
-[struct config_item]
+struct config_item
+==================
+
+::
 
        struct config_item {
                char                    *ci_name;
@@ -168,7 +176,10 @@ By itself, a config_item cannot do much more than appear in configfs.
 Usually a subsystem wants the item to display and/or store attributes,
 among other things.  For that, it needs a type.
 
-[struct config_item_type]
+struct config_item_type
+=======================
+
+::
 
        struct configfs_item_operations {
                void (*release)(struct config_item *);
@@ -192,7 +203,10 @@ allocated dynamically will need to provide the ct_item_ops->release()
 method.  This method is called when the config_item's reference count
 reaches zero.
 
-[struct configfs_attribute]
+struct configfs_attribute
+=========================
+
+::
 
        struct configfs_attribute {
                char                    *ca_name;
@@ -214,7 +228,10 @@ be called whenever userspace asks for a read(2) on the attribute.  If an
 attribute is writable and provides a ->store  method, that method will be
 be called whenever userspace asks for a write(2) on the attribute.
 
-[struct configfs_bin_attribute]
+struct configfs_bin_attribute
+=============================
+
+::
 
        struct configfs_bin_attribute {
                struct configfs_attribute       cb_attr;
@@ -240,11 +257,12 @@ will happen for write(2). The reads/writes are bufferred so only a
 single read/write will occur; the attributes' need not concern itself
 with it.
 
-[struct config_group]
+struct config_group
+===================
 
 A config_item cannot live in a vacuum.  The only way one can be created
 is via mkdir(2) on a config_group.  This will trigger creation of a
-child item.
+child item::
 
        struct config_group {
                struct config_item              cg_item;
@@ -264,7 +282,7 @@ The config_group structure contains a config_item.  Properly configuring
 that item means that a group can behave as an item in its own right.
 However, it can do more: it can create child items or groups.  This is
 accomplished via the group operations specified on the group's
-config_item_type.
+config_item_type::
 
        struct configfs_group_operations {
                struct config_item *(*make_item)(struct config_group *group,
@@ -279,7 +297,8 @@ config_item_type.
        };
 
 A group creates child items by providing the
-ct_group_ops->make_item() method.  If provided, this method is called from mkdir(2) in the group's directory.  The subsystem allocates a new
+ct_group_ops->make_item() method.  If provided, this method is called from
+mkdir(2) in the group's directory.  The subsystem allocates a new
 config_item (or more likely, its container structure), initializes it,
 and returns it to configfs.  Configfs will then populate the filesystem
 tree to reflect the new item.
@@ -296,13 +315,14 @@ upon item allocation.  If a subsystem has no work to do, it may omit
 the ct_group_ops->drop_item() method, and configfs will call
 config_item_put() on the item on behalf of the subsystem.
 
-IMPORTANT: drop_item() is void, and as such cannot fail.  When rmdir(2)
-is called, configfs WILL remove the item from the filesystem tree
-(assuming that it has no children to keep it busy).  The subsystem is
-responsible for responding to this.  If the subsystem has references to
-the item in other threads, the memory is safe.  It may take some time
-for the item to actually disappear from the subsystem's usage.  But it
-is gone from configfs.
+Important:
+   drop_item() is void, and as such cannot fail.  When rmdir(2)
+   is called, configfs WILL remove the item from the filesystem tree
+   (assuming that it has no children to keep it busy).  The subsystem is
+   responsible for responding to this.  If the subsystem has references to
+   the item in other threads, the memory is safe.  It may take some time
+   for the item to actually disappear from the subsystem's usage.  But it
+   is gone from configfs.
 
 When drop_item() is called, the item's linkage has already been torn
 down.  It no longer has a reference on its parent and has no place in
@@ -319,10 +339,11 @@ is implemented in the configfs rmdir(2) code.  ->drop_item() will not be
 called, as the item has not been dropped.  rmdir(2) will fail, as the
 directory is not empty.
 
-[struct configfs_subsystem]
+struct configfs_subsystem
+=========================
 
 A subsystem must register itself, usually at module_init time.  This
-tells configfs to make the subsystem appear in the file tree.
+tells configfs to make the subsystem appear in the file tree::
 
        struct configfs_subsystem {
                struct config_group     su_group;
@@ -332,17 +353,19 @@ tells configfs to make the subsystem appear in the file tree.
        int configfs_register_subsystem(struct configfs_subsystem *subsys);
        void configfs_unregister_subsystem(struct configfs_subsystem *subsys);
 
-       A subsystem consists of a toplevel config_group and a mutex.
+A subsystem consists of a toplevel config_group and a mutex.
 The group is where child config_items are created.  For a subsystem,
 this group is usually defined statically.  Before calling
 configfs_register_subsystem(), the subsystem must have initialized the
 group via the usual group _init() functions, and it must also have
 initialized the mutex.
-       When the register call returns, the subsystem is live, and it
+
+When the register call returns, the subsystem is live, and it
 will be visible via configfs.  At that point, mkdir(2) can be called and
 the subsystem must be ready for it.
 
-[An Example]
+An Example
+==========
 
 The best example of these basic concepts is the simple_children
 subsystem/group and the simple_child item in
@@ -350,7 +373,8 @@ samples/configfs/configfs_sample.c. It shows a trivial object displaying
 and storing an attribute, and a simple group creating and destroying
 these children.
 
-[Hierarchy Navigation and the Subsystem Mutex]
+Hierarchy Navigation and the Subsystem Mutex
+============================================
 
 There is an extra bonus that configfs provides.  The config_groups and
 config_items are arranged in a hierarchy due to the fact that they
@@ -375,7 +399,8 @@ be in its parent's cg_children list for the same duration.  This allows
 a subsystem to trust ci_parent and cg_children while they hold the
 mutex.
 
-[Item Aggregation Via symlink(2)]
+Item Aggregation Via symlink(2)
+===============================
 
 configfs provides a simple group via the group->item parent/child
 relationship.  Often, however, a larger environment requires aggregation
@@ -403,7 +428,8 @@ A config_item cannot be removed while it links to any other item, nor
 can it be removed while an item links to it.  Dangling symlinks are not
 allowed in configfs.
 
-[Automatically Created Subgroups]
+Automatically Created Subgroups
+===============================
 
 A new config_group may want to have two types of child config_items.
 While this could be codified by magic names in ->make_item(), it is much
@@ -433,7 +459,8 @@ As a consequence of this, default groups cannot be removed directly via
 rmdir(2).  They also are not considered when rmdir(2) on the parent
 group is checking for children.
 
-[Dependent Subsystems]
+Dependent Subsystems
+====================
 
 Sometimes other drivers depend on particular configfs items.  For
 example, ocfs2 mounts depend on a heartbeat region item.  If that
@@ -460,9 +487,11 @@ succeeds, then heartbeat knows the region is safe to give to ocfs2.
 If it fails, it was being torn down anyway, and heartbeat can gracefully
 pass up an error.
 
-[Committable Items]
+Committable Items
+=================
 
-NOTE: Committable items are currently unimplemented.
+Note:
+     Committable items are currently unimplemented.
 
 Some config_items cannot have a valid initial state.  That is, no
 default values can be specified for the item's attributes such that the
@@ -504,5 +533,3 @@ As rmdir(2) does not work in the "live" directory, an item must be
 shutdown, or "uncommitted".  Again, this is done via rename(2), this
 time from the "live" directory back to the "pending" one.  The subsystem
 is notified by the ct_group_ops->uncommit_object() method.
-
-
index 679729442fd2d18b78d3a9e504bc7a3f46bba160..735f3859b19f358681e71910f17e3b5227f840bd 100644 (file)
@@ -74,7 +74,7 @@ are zeroed out and converted to written extents before being returned to avoid
 exposure of uninitialized data through mmap.
 
 These filesystems may be used for inspiration:
-- ext2: see Documentation/filesystems/ext2.txt
+- ext2: see Documentation/filesystems/ext2.rst
 - ext4: see Documentation/filesystems/ext4/
 - xfs:  see Documentation/admin-guide/xfs.rst
 
index 6c032db235a5efa2e4745485d4a22cdb3ffe503a..1da7a4b7383d6aac9de2a4727e42f3ad0197280d 100644 (file)
@@ -166,16 +166,17 @@ file::
     };
 
     struct debugfs_regset32 {
-       struct debugfs_reg32 *regs;
+       const struct debugfs_reg32 *regs;
        int nregs;
        void __iomem *base;
+       struct device *dev;     /* Optional device for Runtime PM */
     };
 
     debugfs_create_regset32(const char *name, umode_t mode,
                            struct dentry *parent,
                            struct debugfs_regset32 *regset);
 
-    void debugfs_print_regs32(struct seq_file *s, struct debugfs_reg32 *regs,
+    void debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs,
                         int nregs, void __iomem *base, char *prefix);
 
 The "base" argument may be 0, but you may want to build the reg32 array
diff --git a/Documentation/filesystems/devpts.rst b/Documentation/filesystems/devpts.rst
new file mode 100644 (file)
index 0000000..a03248d
--- /dev/null
@@ -0,0 +1,36 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+The Devpts Filesystem
+=====================
+
+Each mount of the devpts filesystem is now distinct such that ptys
+and their indicies allocated in one mount are independent from ptys
+and their indicies in all other mounts.
+
+All mounts of the devpts filesystem now create a ``/dev/pts/ptmx`` node
+with permissions ``0000``.
+
+To retain backwards compatibility the a ptmx device node (aka any node
+created with ``mknod name c 5 2``) when opened will look for an instance
+of devpts under the name ``pts`` in the same directory as the ptmx device
+node.
+
+As an option instead of placing a ``/dev/ptmx`` device node at ``/dev/ptmx``
+it is possible to place a symlink to ``/dev/pts/ptmx`` at ``/dev/ptmx`` or
+to bind mount ``/dev/ptx/ptmx`` to ``/dev/ptmx``.  If you opt for using
+the devpts filesystem in this manner devpts should be mounted with
+the ``ptmxmode=0666``, or ``chmod 0666 /dev/pts/ptmx`` should be called.
+
+Total count of pty pairs in all instances is limited by sysctls::
+
+    kernel.pty.max = 4096      - global limit
+    kernel.pty.reserve = 1024  - reserved for filesystems mounted from the initial mount namespace
+    kernel.pty.nr              - current count of ptys
+
+Per-instance limit could be set by adding mount option ``max=<count>``.
+
+This feature was added in kernel 3.4 together with
+``sysctl kernel.pty.reserve``.
+
+In kernels older than 3.4 sysctl ``kernel.pty.max`` works as per-instance limit.
diff --git a/Documentation/filesystems/devpts.txt b/Documentation/filesystems/devpts.txt
deleted file mode 100644 (file)
index 9f94fe2..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-Each mount of the devpts filesystem is now distinct such that ptys
-and their indicies allocated in one mount are independent from ptys
-and their indicies in all other mounts.
-
-All mounts of the devpts filesystem now create a /dev/pts/ptmx node
-with permissions 0000.
-
-To retain backwards compatibility the a ptmx device node (aka any node
-created with "mknod name c 5 2") when opened will look for an instance
-of devpts under the name "pts" in the same directory as the ptmx device
-node.
-
-As an option instead of placing a /dev/ptmx device node at /dev/ptmx
-it is possible to place a symlink to /dev/pts/ptmx at /dev/ptmx or
-to bind mount /dev/ptx/ptmx to /dev/ptmx.  If you opt for using
-the devpts filesystem in this manner devpts should be mounted with
-the ptmxmode=0666, or chmod 0666 /dev/pts/ptmx should be called.
-
-Total count of pty pairs in all instances is limited by sysctls:
-kernel.pty.max = 4096          - global limit
-kernel.pty.reserve = 1024      - reserved for filesystems mounted from the initial mount namespace
-kernel.pty.nr                  - current count of ptys
-
-Per-instance limit could be set by adding mount option "max=<count>".
-This feature was added in kernel 3.4 together with sysctl kernel.pty.reserve.
-In kernels older than 3.4 sysctl kernel.pty.max works as per-instance limit.
similarity index 88%
rename from Documentation/filesystems/dnotify.txt
rename to Documentation/filesystems/dnotify.rst
index 15156883d321995aa34c12126ff3186c5903b44d..a28a1f9ef79c05812b9d6e81f96e55cf74d1910d 100644 (file)
@@ -1,5 +1,8 @@
-               Linux Directory Notification
-               ============================
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+Linux Directory Notification
+============================
 
           Stephen Rothwell <sfr@canb.auug.org.au>
 
@@ -12,6 +15,7 @@ being delivered using signals.
 The application decides which "events" it wants to be notified about.
 The currently defined events are:
 
+       =========       =====================================================
        DN_ACCESS       A file in the directory was accessed (read)
        DN_MODIFY       A file in the directory was modified (write,truncate)
        DN_CREATE       A file was created in the directory
@@ -19,6 +23,7 @@ The currently defined events are:
        DN_RENAME       A file in the directory was renamed
        DN_ATTRIB       A file in the directory had its attributes
                        changed (chmod,chown)
+       =========       =====================================================
 
 Usually, the application must reregister after each notification, but
 if DN_MULTISHOT is or'ed with the event mask, then the registration will
@@ -36,7 +41,7 @@ especially important if DN_MULTISHOT is specified.  Note that SIGRTMIN
 is often blocked, so it is better to use (at least) SIGRTMIN + 1.
 
 Implementation expectations (features and bugs :-))
----------------------------
+---------------------------------------------------
 
 The notification should work for any local access to files even if the
 actual file system is on a remote server.  This implies that remote
@@ -67,4 +72,4 @@ See tools/testing/selftests/filesystems/dnotify_test.c for an example.
 NOTE
 ----
 Beginning with Linux 2.6.13, dnotify has been replaced by inotify.
-See Documentation/filesystems/inotify.txt for more information on it.
+See Documentation/filesystems/inotify.rst for more information on it.
index 90ac65683e7e9719501884421f33b9031ab2846a..0551985821b889b064b909737bccd12f7d98cf01 100644 (file)
@@ -24,3 +24,20 @@ files that are not well-known standardized variables are created
 as immutable files.  This doesn't prevent removal - "chattr -i" will work -
 but it does prevent this kind of failure from being accomplished
 accidentally.
+
+.. warning ::
+      When a content of an UEFI variable in /sys/firmware/efi/efivars is
+      displayed, for example using "hexdump", pay attention that the first
+      4 bytes of the output represent the UEFI variable attributes,
+      in little-endian format.
+
+      Practically the output of each efivar is composed of:
+
+          +-----------------------------------+
+          |4_bytes_of_attributes + efivar_data|
+          +-----------------------------------+
+
+*See also:*
+
+- Documentation/admin-guide/acpi/ssdt-overlays.rst
+- Documentation/ABI/stable/sysfs-firmware-efi-vars
index 87d794bc75a4790ced1e0fcf6ef9f3b4aaa11081..4218ac65862934c13674527711beb12996c24137 100644 (file)
@@ -225,8 +225,12 @@ fsync_mode=%s          Control the policy of fsync. Currently supports "posix",
                        pass, but the performance will regress. "nobarrier" is
                        based on "posix", but doesn't issue flush command for
                        non-atomic files likewise "nobarrier" mount option.
-test_dummy_encryption  Enable dummy encryption, which provides a fake fscrypt
+test_dummy_encryption
+test_dummy_encryption=%s
+                       Enable dummy encryption, which provides a fake fscrypt
                        context. The fake fscrypt context is used by xfstests.
+                       The argument may be either "v1" or "v2", in order to
+                       select the corresponding fscrypt policy version.
 checkpoint=%s[:%u[%]]  Set to "disable" to turn off checkpointing. Set to "enable"
                        to reenable checkpointing. Is enabled by default. While
                        disabled, any unmounting or unexpected shutdowns will cause
similarity index 70%
rename from Documentation/filesystems/fiemap.txt
rename to Documentation/filesystems/fiemap.rst
index ac87e6fda842b4dab2898095ff6fc3a14e870e1d..2a572e7edc0887c83216b23fcb53ba06770abf7f 100644 (file)
@@ -1,3 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
 ============
 Fiemap Ioctl
 ============
@@ -10,9 +12,9 @@ returns a list of extents.
 Request Basics
 --------------
 
-A fiemap request is encoded within struct fiemap:
+A fiemap request is encoded within struct fiemap::
 
-struct fiemap {
+  struct fiemap {
        __u64   fm_start;        /* logical offset (inclusive) at
                                  * which to start mapping (in) */
        __u64   fm_length;       /* logical length of mapping which
@@ -23,7 +25,7 @@ struct fiemap {
        __u32   fm_extent_count; /* size of fm_extents array (in) */
        __u32   fm_reserved;
        struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
-};
+  };
 
 
 fm_start, and fm_length specify the logical range within the file
@@ -51,12 +53,12 @@ nothing to prevent the file from changing between calls to FIEMAP.
 
 The following flags can be set in fm_flags:
 
-FIEMAP_FLAG_SYNC
-If this flag is set, the kernel will sync the file before mapping extents.
+FIEMAP_FLAG_SYNC
+  If this flag is set, the kernel will sync the file before mapping extents.
 
-FIEMAP_FLAG_XATTR
-If this flag is set, the extents returned will describe the inodes
-extended attribute lookup tree, instead of its data tree.
+FIEMAP_FLAG_XATTR
+  If this flag is set, the extents returned will describe the inodes
+  extended attribute lookup tree, instead of its data tree.
 
 
 Extent Mapping
@@ -75,18 +77,18 @@ complete the requested range and will not have the FIEMAP_EXTENT_LAST
 flag set (see the next section on extent flags).
 
 Each extent is described by a single fiemap_extent structure as
-returned in fm_extents.
-
-struct fiemap_extent {
-       __u64   fe_logical;  /* logical offset in bytes for the start of
-                             * the extent */
-       __u64   fe_physical; /* physical offset in bytes for the start
-                             * of the extent */
-       __u64   fe_length;   /* length in bytes for the extent */
-       __u64   fe_reserved64[2];
-       __u32   fe_flags;    /* FIEMAP_EXTENT_* flags for this extent */
-       __u32   fe_reserved[3];
-};
+returned in fm_extents::
+
+    struct fiemap_extent {
+           __u64       fe_logical;  /* logical offset in bytes for the start of
+                               * the extent */
+           __u64       fe_physical; /* physical offset in bytes for the start
+                               * of the extent */
+           __u64       fe_length;   /* length in bytes for the extent */
+           __u64       fe_reserved64[2];
+           __u32       fe_flags;    /* FIEMAP_EXTENT_* flags for this extent */
+           __u32       fe_reserved[3];
+    };
 
 All offsets and lengths are in bytes and mirror those on disk.  It is valid
 for an extents logical offset to start before the request or its logical
@@ -114,26 +116,27 @@ worry about all present and future flags which might imply unaligned
 data. Note that the opposite is not true - it would be valid for
 FIEMAP_EXTENT_NOT_ALIGNED to appear alone.
 
-* FIEMAP_EXTENT_LAST
-This is generally the last extent in the file. A mapping attempt past
-this extent may return nothing. Some implementations set this flag to
-indicate this extent is the last one in the range queried by the user
-(via fiemap->fm_length).
+FIEMAP_EXTENT_LAST
+  This is generally the last extent in the file. A mapping attempt past
+  this extent may return nothing. Some implementations set this flag to
+  indicate this extent is the last one in the range queried by the user
+  (via fiemap->fm_length).
+
+FIEMAP_EXTENT_UNKNOWN
+  The location of this extent is currently unknown. This may indicate
+  the data is stored on an inaccessible volume or that no storage has
+  been allocated for the file yet.
 
-* FIEMAP_EXTENT_UNKNOWN
-The location of this extent is currently unknown. This may indicate
-the data is stored on an inaccessible volume or that no storage has
-been allocated for the file yet.
+FIEMAP_EXTENT_DELALLOC
+  This will also set FIEMAP_EXTENT_UNKNOWN.
 
-* FIEMAP_EXTENT_DELALLOC
-  - This will also set FIEMAP_EXTENT_UNKNOWN.
-Delayed allocation - while there is data for this extent, its
-physical location has not been allocated yet.
+  Delayed allocation - while there is data for this extent, its
+  physical location has not been allocated yet.
 
-FIEMAP_EXTENT_ENCODED
-This extent does not consist of plain filesystem blocks but is
-encoded (e.g. encrypted or compressed).  Reading the data in this
-extent via I/O to the block device will have undefined results.
+FIEMAP_EXTENT_ENCODED
+  This extent does not consist of plain filesystem blocks but is
+  encoded (e.g. encrypted or compressed).  Reading the data in this
+  extent via I/O to the block device will have undefined results.
 
 Note that it is *always* undefined to try to update the data
 in-place by writing to the indicated location without the
@@ -145,32 +148,32 @@ unmounted, and then only if the FIEMAP_EXTENT_ENCODED flag is
 clear; user applications must not try reading or writing to the
 filesystem via the block device under any other circumstances.
 
-FIEMAP_EXTENT_DATA_ENCRYPTED
-  This will also set FIEMAP_EXTENT_ENCODED
-The data in this extent has been encrypted by the file system.
+FIEMAP_EXTENT_DATA_ENCRYPTED
+  This will also set FIEMAP_EXTENT_ENCODED
+  The data in this extent has been encrypted by the file system.
 
-FIEMAP_EXTENT_NOT_ALIGNED
-Extent offsets and length are not guaranteed to be block aligned.
+FIEMAP_EXTENT_NOT_ALIGNED
+  Extent offsets and length are not guaranteed to be block aligned.
 
-FIEMAP_EXTENT_DATA_INLINE
+FIEMAP_EXTENT_DATA_INLINE
   This will also set FIEMAP_EXTENT_NOT_ALIGNED
-Data is located within a meta data block.
+  Data is located within a meta data block.
 
-FIEMAP_EXTENT_DATA_TAIL
+FIEMAP_EXTENT_DATA_TAIL
   This will also set FIEMAP_EXTENT_NOT_ALIGNED
-Data is packed into a block with data from other files.
+  Data is packed into a block with data from other files.
 
-FIEMAP_EXTENT_UNWRITTEN
-Unwritten extent - the extent is allocated but its data has not been
-initialized.  This indicates the extent's data will be all zero if read
-through the filesystem but the contents are undefined if read directly from
-the device.
+FIEMAP_EXTENT_UNWRITTEN
+  Unwritten extent - the extent is allocated but its data has not been
+  initialized.  This indicates the extent's data will be all zero if read
+  through the filesystem but the contents are undefined if read directly from
+  the device.
 
-FIEMAP_EXTENT_MERGED
-This will be set when a file does not support extents, i.e., it uses a block
-based addressing scheme.  Since returning an extent for each block back to
-userspace would be highly inefficient, the kernel will try to merge most
-adjacent blocks into 'extents'.
+FIEMAP_EXTENT_MERGED
+  This will be set when a file does not support extents, i.e., it uses a block
+  based addressing scheme.  Since returning an extent for each block back to
+  userspace would be highly inefficient, the kernel will try to merge most
+  adjacent blocks into 'extents'.
 
 
 VFS -> File System Implementation
@@ -179,23 +182,23 @@ VFS -> File System Implementation
 File systems wishing to support fiemap must implement a ->fiemap callback on
 their inode_operations structure. The fs ->fiemap call is responsible for
 defining its set of supported fiemap flags, and calling a helper function on
-each discovered extent:
+each discovered extent::
 
-struct inode_operations {
+  struct inode_operations {
        ...
 
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
                      u64 len);
 
 ->fiemap is passed struct fiemap_extent_info which describes the
-fiemap request:
+fiemap request::
 
-struct fiemap_extent_info {
+  struct fiemap_extent_info {
        unsigned int fi_flags;          /* Flags as passed from user */
        unsigned int fi_extents_mapped; /* Number of mapped extents */
        unsigned int fi_extents_max;    /* Size of fiemap_extent array */
        struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent array */
-};
+  };
 
 It is intended that the file system should not need to access any of this
 structure directly. Filesystem handlers should be tolerant to signals and return
@@ -203,9 +206,9 @@ EINTR once fatal signal received.
 
 
 Flag checking should be done at the beginning of the ->fiemap callback via the
-fiemap_check_flags() helper:
+fiemap_check_flags() helper::
 
-int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
+  int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
 
 The struct fieinfo should be passed in as received from ioctl_fiemap(). The
 set of fiemap flags which the fs understands should be passed via fs_flags. If
@@ -216,10 +219,10 @@ ioctl_fiemap().
 
 
 For each extent in the request range, the file system should call
-the helper function, fiemap_fill_next_extent():
+the helper function, fiemap_fill_next_extent()::
 
-int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
-                           u64 phys, u64 len, u32 flags, u32 dev);
+  int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
+                             u64 phys, u64 len, u32 flags, u32 dev);
 
 fiemap_fill_next_extent() will use the passed values to populate the
 next free extent in the fm_extents array. 'General' extent flags will
similarity index 95%
rename from Documentation/filesystems/files.txt
rename to Documentation/filesystems/files.rst
index 46dfc6b038c3fd57892e272de065e51a025c64f3..cbf8e57376bf681ee697c2499726fb946d2dd8ca 100644 (file)
@@ -1,5 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
 File management in the Linux kernel
------------------------------------
+===================================
 
 This document describes how locking for files (struct file)
 and file descriptor table (struct files) works.
@@ -34,7 +37,7 @@ appear atomic. Here are the locking rules for
 the fdtable structure -
 
 1. All references to the fdtable must be done through
-   the files_fdtable() macro :
+   the files_fdtable() macro::
 
        struct fdtable *fdt;
 
@@ -61,7 +64,8 @@ the fdtable structure -
 4. To look up the file structure given an fd, a reader
    must use either fcheck() or fcheck_files() APIs. These
    take care of barrier requirements due to lock-free lookup.
-   An example :
+
+   An example::
 
        struct file *file;
 
@@ -77,7 +81,7 @@ the fdtable structure -
    of the fd (fget()/fget_light()) are lock-free, it is possible
    that look-up may race with the last put() operation on the
    file structure. This is avoided using atomic_long_inc_not_zero()
-   on ->f_count :
+   on ->f_count::
 
        rcu_read_lock();
        file = fcheck_files(files, fd);
@@ -106,7 +110,8 @@ the fdtable structure -
    holding files->file_lock. If ->file_lock is dropped, then
    another thread expand the files thereby creating a new
    fdtable and making the earlier fdtable pointer stale.
-   For example :
+
+   For example::
 
        spin_lock(&files->file_lock);
        fd = locate_fd(files, file, start);
index aa072112cfff2eb5eeb12e8a63ac28ee0490ad6e..f517af8ec11c8849621d8654fb4ec0149b87df6a 100644 (file)
@@ -292,8 +292,22 @@ files' data differently, inode numbers are included in the IVs.
 Consequently, shrinking the filesystem may not be allowed.
 
 This format is optimized for use with inline encryption hardware
-compliant with the UFS or eMMC standards, which support only 64 IV
-bits per I/O request and may have only a small number of keyslots.
+compliant with the UFS standard, which supports only 64 IV bits per
+I/O request and may have only a small number of keyslots.
+
+IV_INO_LBLK_32 policies
+-----------------------
+
+IV_INO_LBLK_32 policies work like IV_INO_LBLK_64, except that for
+IV_INO_LBLK_32, the inode number is hashed with SipHash-2-4 (where the
+SipHash key is derived from the master key) and added to the file
+logical block number mod 2^32 to produce a 32-bit IV.
+
+This format is optimized for use with inline encryption hardware
+compliant with the eMMC v5.2 standard, which supports only 32 IV bits
+per I/O request and may have only a small number of keyslots.  This
+format results in some level of IV reuse, so it should only be used
+when necessary due to hardware limitations.
 
 Key identifiers
 ---------------
@@ -369,6 +383,10 @@ a little endian number, except that:
   to 32 bits and is placed in bits 0-31 of the IV.  The inode number
   (which is also limited to 32 bits) is placed in bits 32-63.
 
+- With `IV_INO_LBLK_32 policies`_, the logical block number is limited
+  to 32 bits and is placed in bits 0-31 of the IV.  The inode number
+  is then hashed and added mod 2^32.
+
 Note that because file logical block numbers are included in the IVs,
 filesystems must enforce that blocks are never shifted around within
 encrypted files, e.g. via "collapse range" or "insert range".
@@ -465,8 +483,15 @@ This structure must be initialized as follows:
     (0x3).
   - FSCRYPT_POLICY_FLAG_DIRECT_KEY: See `DIRECT_KEY policies`_.
   - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64: See `IV_INO_LBLK_64
-    policies`_.  This is mutually exclusive with DIRECT_KEY and is not
-    supported on v1 policies.
+    policies`_.
+  - FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32: See `IV_INO_LBLK_32
+    policies`_.
+
+  v1 encryption policies only support the PAD_* and DIRECT_KEY flags.
+  The other flags are only supported by v2 encryption policies.
+
+  The DIRECT_KEY, IV_INO_LBLK_64, and IV_INO_LBLK_32 flags are
+  mutually exclusive.
 
 - For v2 encryption policies, ``__reserved`` must be zeroed.
 
similarity index 95%
rename from Documentation/filesystems/fuse-io.txt
rename to Documentation/filesystems/fuse-io.rst
index 07b8f73f100f689263d148563528b82c2a6948c6..255a368fe534b4582c9be673523330e962803123 100644 (file)
@@ -1,3 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============
+Fuse I/O Modes
+==============
+
 Fuse supports the following I/O modes:
 
 - direct-io
index e7b46dac70791efebdb114d512f2fd495f8df051..17795341e0a36bc50f8a6956d1470e2b0d9fbd6f 100644 (file)
@@ -24,6 +24,22 @@ algorithms work.
    splice
    locking
    directory-locking
+   devpts
+   dnotify
+   fiemap
+   files
+   locks
+   mandatory-locking
+   mount_api
+   quota
+   seq_file
+   sharedsubtree
+   sysfs-pci
+   sysfs-tagging
+
+   automount-support
+
+   caching/index
 
    porting
 
@@ -57,7 +73,10 @@ Documentation for filesystem implementations.
    befs
    bfs
    btrfs
+   cifs/cifsroot
    ceph
+   coda
+   configfs
    cramfs
    debugfs
    dlmfs
@@ -73,6 +92,7 @@ Documentation for filesystem implementations.
    hfsplus
    hpfs
    fuse
+   fuse-io
    inotify
    isofs
    nilfs2
@@ -88,6 +108,7 @@ Documentation for filesystem implementations.
    ramfs-rootfs-initramfs
    relay
    romfs
+   spufs/index
    squashfs
    sysfs
    sysv-fs
@@ -97,4 +118,6 @@ Documentation for filesystem implementations.
    udf
    virtiofs
    vfat
+   xfs-delayed-logging-design
+   xfs-self-describing-metadata
    zonefs
index 5057e4d9dcd1d6c8799dafac8ef0ebab022e695d..0af2e0e11461d04bc9a6c33377e29474c945f67e 100644 (file)
@@ -239,6 +239,7 @@ prototypes::
        int (*readpage)(struct file *, struct page *);
        int (*writepages)(struct address_space *, struct writeback_control *);
        int (*set_page_dirty)(struct page *page);
+       void (*readahead)(struct readahead_control *);
        int (*readpages)(struct file *filp, struct address_space *mapping,
                        struct list_head *pages, unsigned nr_pages);
        int (*write_begin)(struct file *, struct address_space *mapping,
@@ -271,7 +272,8 @@ writepage:          yes, unlocks (see below)
 readpage:              yes, unlocks
 writepages:
 set_page_dirty         no
-readpages:
+readahead:             yes, unlocks
+readpages:             no
 write_begin:           locks the page           exclusive
 write_end:             yes, unlocks             exclusive
 bmap:
@@ -295,6 +297,8 @@ the request handler (/dev/loop).
 ->readpage() unlocks the page, either synchronously or via I/O
 completion.
 
+->readahead() unlocks the pages that I/O is attempted on like ->readpage().
+
 ->readpages() populates the pagecache with the passed pages and starts
 I/O against them.  They come unlocked upon I/O completion.
 
similarity index 91%
rename from Documentation/filesystems/locks.txt
rename to Documentation/filesystems/locks.rst
index 5368690f412e5063aa1dff7c7ef44a090fd5207d..c5ae858b1aacb7e87c8d45c3f65e97e771bcd15d 100644 (file)
@@ -1,4 +1,8 @@
-                     File Locking Release Notes
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
+File Locking Release Notes
+==========================
 
                Andy Walker <andy@lysaker.kvaerner.no>
 
@@ -6,7 +10,7 @@
 
 
 1. What's New?
---------------
+==============
 
 1.1 Broken Flock Emulation
 --------------------------
@@ -25,7 +29,7 @@ anyway (see the file "Documentation/process/changes.rst".)
 ---------------------------
 
 1.2.1 Typical Problems - Sendmail
----------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 Because sendmail was unable to use the old flock() emulation, many sendmail
 installations use fcntl() instead of flock(). This is true of Slackware 3.0
 for example. This gave rise to some other subtle problems if sendmail was
@@ -37,7 +41,7 @@ to lock solid with deadlocked processes.
 
 
 1.2.2 The Solution
-------------------
+^^^^^^^^^^^^^^^^^^
 The solution I have chosen, after much experimentation and discussion,
 is to make flock() and fcntl() locks oblivious to each other. Both can
 exists, and neither will have any effect on the other.
@@ -54,7 +58,7 @@ fcntl(), with all the problems that implies.
 ---------------------------------------
 
 Mandatory locking, as described in
-'Documentation/filesystems/mandatory-locking.txt' was prior to this release a
+'Documentation/filesystems/mandatory-locking.rst' was prior to this release a
 general configuration option that was valid for all mounted filesystems.  This
 had a number of inherent dangers, not the least of which was the ability to
 freeze an NFS server by asking it to read a file for which a mandatory lock
similarity index 91%
rename from Documentation/filesystems/mandatory-locking.txt
rename to Documentation/filesystems/mandatory-locking.rst
index a251ca33164ae9d2e8902f94aeb680e7038faeae..9ce73544a8f02ba516eb2e3ba115b9a1aa8ae854 100644 (file)
@@ -1,8 +1,13 @@
-       Mandatory File Locking For The Linux Operating System
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================================
+Mandatory File Locking For The Linux Operating System
+=====================================================
 
                Andy Walker <andy@lysaker.kvaerner.no>
 
                           15 April 1996
+
                     (Updated September 2007)
 
 0. Why you should avoid mandatory locking
@@ -53,15 +58,17 @@ possible on existing user code. The scheme is based on marking individual files
 as candidates for mandatory locking, and using the existing fcntl()/lockf()
 interface for applying locks just as if they were normal, advisory locks.
 
-Note 1: In saying "file" in the paragraphs above I am actually not telling
-the whole truth. System V locking is based on fcntl(). The granularity of
-fcntl() is such that it allows the locking of byte ranges in files, in addition
-to entire files, so the mandatory locking rules also have byte level
-granularity.
+.. Note::
+
+   1. In saying "file" in the paragraphs above I am actually not telling
+      the whole truth. System V locking is based on fcntl(). The granularity of
+      fcntl() is such that it allows the locking of byte ranges in files, in
+      addition to entire files, so the mandatory locking rules also have byte
+      level granularity.
 
-Note 2: POSIX.1 does not specify any scheme for mandatory locking, despite
-borrowing the fcntl() locking scheme from System V. The mandatory locking
-scheme is defined by the System V Interface Definition (SVID) Version 3.
+   2. POSIX.1 does not specify any scheme for mandatory locking, despite
+      borrowing the fcntl() locking scheme from System V. The mandatory locking
+      scheme is defined by the System V Interface Definition (SVID) Version 3.
 
 2. Marking a file for mandatory locking
 ---------------------------------------
similarity index 79%
rename from Documentation/filesystems/mount_api.txt
rename to Documentation/filesystems/mount_api.rst
index 87c14bbb2b35a052908431345acf6215c0f1f95b..dea22d64f060ab044264dba49ac2c936990b01ea 100644 (file)
@@ -1,8 +1,10 @@
-                            ====================
-                            FILESYSTEM MOUNT API
-                            ====================
+.. SPDX-License-Identifier: GPL-2.0
 
-CONTENTS
+====================
+fILESYSTEM Mount API
+====================
+
+.. CONTENTS
 
  (1) Overview.
 
@@ -21,8 +23,7 @@ CONTENTS
  (8) Parameter helper functions.
 
 
-========
-OVERVIEW
+Overview
 ========
 
 The creation of new mounts is now to be done in a multistep process:
@@ -43,7 +44,7 @@ The creation of new mounts is now to be done in a multistep process:
 
  (7) Destroy the context.
 
-To support this, the file_system_type struct gains two new fields:
+To support this, the file_system_type struct gains two new fields::
 
        int (*init_fs_context)(struct fs_context *fc);
        const struct fs_parameter_description *parameters;
@@ -57,12 +58,11 @@ Note that security initialisation is done *after* the filesystem is called so
 that the namespaces may be adjusted first.
 
 
-======================
-THE FILESYSTEM CONTEXT
+The Filesystem context
 ======================
 
 The creation and reconfiguration of a superblock is governed by a filesystem
-context.  This is represented by the fs_context structure:
+context.  This is represented by the fs_context structure::
 
        struct fs_context {
                const struct fs_context_operations *ops;
@@ -86,78 +86,106 @@ context.  This is represented by the fs_context structure:
 
 The fs_context fields are as follows:
 
- (*) const struct fs_context_operations *ops
+   * ::
+
+       const struct fs_context_operations *ops
 
      These are operations that can be done on a filesystem context (see
      below).  This must be set by the ->init_fs_context() file_system_type
      operation.
 
- (*) struct file_system_type *fs_type
+   * ::
+
+       struct file_system_type *fs_type
 
      A pointer to the file_system_type of the filesystem that is being
      constructed or reconfigured.  This retains a reference on the type owner.
 
- (*) void *fs_private
+   * ::
+
+       void *fs_private
 
      A pointer to the file system's private data.  This is where the filesystem
      will need to store any options it parses.
 
- (*) struct dentry *root
+   * ::
+
+       struct dentry *root
 
      A pointer to the root of the mountable tree (and indirectly, the
      superblock thereof).  This is filled in by the ->get_tree() op.  If this
      is set, an active reference on root->d_sb must also be held.
 
- (*) struct user_namespace *user_ns
- (*) struct net *net_ns
+   * ::
+
+       struct user_namespace *user_ns
+       struct net *net_ns
 
      There are a subset of the namespaces in use by the invoking process.  They
      retain references on each namespace.  The subscribed namespaces may be
      replaced by the filesystem to reflect other sources, such as the parent
      mount superblock on an automount.
 
- (*) const struct cred *cred
+   * ::
+
+       const struct cred *cred
 
      The mounter's credentials.  This retains a reference on the credentials.
 
- (*) char *source
+   * ::
+
+       char *source
 
      This specifies the source.  It may be a block device (e.g. /dev/sda1) or
      something more exotic, such as the "host:/path" that NFS desires.
 
- (*) char *subtype
+   * ::
+
+       char *subtype
 
      This is a string to be added to the type displayed in /proc/mounts to
      qualify it (used by FUSE).  This is available for the filesystem to set if
      desired.
 
- (*) void *security
+   * ::
+
+       void *security
 
      A place for the LSMs to hang their security data for the superblock.  The
      relevant security operations are described below.
 
- (*) void *s_fs_info
+   * ::
+
+       void *s_fs_info
 
      The proposed s_fs_info for a new superblock, set in the superblock by
      sget_fc().  This can be used to distinguish superblocks.
 
- (*) unsigned int sb_flags
- (*) unsigned int sb_flags_mask
+   * ::
+
+       unsigned int sb_flags
+       unsigned int sb_flags_mask
 
      Which bits SB_* flags are to be set/cleared in super_block::s_flags.
 
- (*) unsigned int s_iflags
+   * ::
+
+       unsigned int s_iflags
 
      These will be bitwise-OR'd with s->s_iflags when a superblock is created.
 
- (*) enum fs_context_purpose
+   * ::
+
+       enum fs_context_purpose
 
      This indicates the purpose for which the context is intended.  The
      available values are:
 
-       FS_CONTEXT_FOR_MOUNT,           -- New superblock for explicit mount
-       FS_CONTEXT_FOR_SUBMOUNT         -- New automatic submount of extant mount
-       FS_CONTEXT_FOR_RECONFIGURE      -- Change an existing mount
+       ==========================      ======================================
+       FS_CONTEXT_FOR_MOUNT,           New superblock for explicit mount
+       FS_CONTEXT_FOR_SUBMOUNT         New automatic submount of extant mount
+       FS_CONTEXT_FOR_RECONFIGURE      Change an existing mount
+       ==========================      ======================================
 
 The mount context is created by calling vfs_new_fs_context() or
 vfs_dup_fs_context() and is destroyed with put_fs_context().  Note that the
@@ -176,11 +204,10 @@ mount context.  For instance, NFS might pin the appropriate protocol version
 module.
 
 
-=================================
-THE FILESYSTEM CONTEXT OPERATIONS
+The Filesystem Context Operations
 =================================
 
-The filesystem context points to a table of operations:
+The filesystem context points to a table of operations::
 
        struct fs_context_operations {
                void (*free)(struct fs_context *fc);
@@ -195,24 +222,32 @@ The filesystem context points to a table of operations:
 These operations are invoked by the various stages of the mount procedure to
 manage the filesystem context.  They are as follows:
 
- (*) void (*free)(struct fs_context *fc);
+   * ::
+
+       void (*free)(struct fs_context *fc);
 
      Called to clean up the filesystem-specific part of the filesystem context
      when the context is destroyed.  It should be aware that parts of the
      context may have been removed and NULL'd out by ->get_tree().
 
- (*) int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
+   * ::
+
+       int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
 
      Called when a filesystem context has been duplicated to duplicate the
      filesystem-private data.  An error may be returned to indicate failure to
      do this.
 
-     [!] Note that even if this fails, put_fs_context() will be called
+     .. Warning::
+
+         Note that even if this fails, put_fs_context() will be called
         immediately thereafter, so ->dup() *must* make the
         filesystem-private data safe for ->free().
 
- (*) int (*parse_param)(struct fs_context *fc,
-                       struct struct fs_parameter *param);
+   * ::
+
+       int (*parse_param)(struct fs_context *fc,
+                          struct struct fs_parameter *param);
 
      Called when a parameter is being added to the filesystem context.  param
      points to the key name and maybe a value object.  VFS-specific options
@@ -224,7 +259,9 @@ manage the filesystem context.  They are as follows:
 
      If successful, 0 should be returned or a negative error code otherwise.
 
- (*) int (*parse_monolithic)(struct fs_context *fc, void *data);
+   * ::
+
+       int (*parse_monolithic)(struct fs_context *fc, void *data);
 
      Called when the mount(2) system call is invoked to pass the entire data
      page in one go.  If this is expected to be just a list of "key[=val]"
@@ -236,7 +273,9 @@ manage the filesystem context.  They are as follows:
      finds it's the standard key-val list then it may pass it off to
      generic_parse_monolithic().
 
- (*) int (*get_tree)(struct fs_context *fc);
+   * ::
+
+       int (*get_tree)(struct fs_context *fc);
 
      Called to get or create the mountable root and superblock, using the
      information stored in the filesystem context (reconfiguration goes via a
@@ -249,7 +288,9 @@ manage the filesystem context.  They are as follows:
      The phase on a userspace-driven context will be set to only allow this to
      be called once on any particular context.
 
- (*) int (*reconfigure)(struct fs_context *fc);
+   * ::
+
+       int (*reconfigure)(struct fs_context *fc);
 
      Called to effect reconfiguration of a superblock using information stored
      in the filesystem context.  It may detach any resources it desires from
@@ -259,19 +300,20 @@ manage the filesystem context.  They are as follows:
      On success it should return 0.  In the case of an error, it should return
      a negative error code.
 
-     [NOTE] reconfigure is intended as a replacement for remount_fs.
+     .. Note:: reconfigure is intended as a replacement for remount_fs.
 
 
-===========================
-FILESYSTEM CONTEXT SECURITY
+Filesystem context Security
 ===========================
 
 The filesystem context contains a security pointer that the LSMs can use for
 building up a security context for the superblock to be mounted.  There are a
 number of operations used by the new mount code for this purpose:
 
- (*) int security_fs_context_alloc(struct fs_context *fc,
-                                  struct dentry *reference);
+   * ::
+
+       int security_fs_context_alloc(struct fs_context *fc,
+                                     struct dentry *reference);
 
      Called to initialise fc->security (which is preset to NULL) and allocate
      any resources needed.  It should return 0 on success or a negative error
@@ -283,22 +325,28 @@ number of operations used by the new mount code for this purpose:
      non-NULL in the case of a submount (FS_CONTEXT_FOR_SUBMOUNT) in which case
      it indicates the automount point.
 
- (*) int security_fs_context_dup(struct fs_context *fc,
-                                struct fs_context *src_fc);
+   * ::
+
+       int security_fs_context_dup(struct fs_context *fc,
+                                   struct fs_context *src_fc);
 
      Called to initialise fc->security (which is preset to NULL) and allocate
      any resources needed.  The original filesystem context is pointed to by
      src_fc and may be used for reference.  It should return 0 on success or a
      negative error code on failure.
 
- (*) void security_fs_context_free(struct fs_context *fc);
+   * ::
+
+       void security_fs_context_free(struct fs_context *fc);
 
      Called to clean up anything attached to fc->security.  Note that the
      contents may have been transferred to a superblock and the pointer cleared
      during get_tree.
 
- (*) int security_fs_context_parse_param(struct fs_context *fc,
-                                        struct fs_parameter *param);
+   * ::
+
+       int security_fs_context_parse_param(struct fs_context *fc,
+                                           struct fs_parameter *param);
 
      Called for each mount parameter, including the source.  The arguments are
      as for the ->parse_param() method.  It should return 0 to indicate that
@@ -310,7 +358,9 @@ number of operations used by the new mount code for this purpose:
      (provided the value pointer is NULL'd out).  If it is stolen, 1 must be
      returned to prevent it being passed to the filesystem.
 
- (*) int security_fs_context_validate(struct fs_context *fc);
+   * ::
+
+       int security_fs_context_validate(struct fs_context *fc);
 
      Called after all the options have been parsed to validate the collection
      as a whole and to do any necessary allocation so that
@@ -320,36 +370,43 @@ number of operations used by the new mount code for this purpose:
      In the case of reconfiguration, the target superblock will be accessible
      via fc->root.
 
- (*) int security_sb_get_tree(struct fs_context *fc);
+   * ::
+
+       int security_sb_get_tree(struct fs_context *fc);
 
      Called during the mount procedure to verify that the specified superblock
      is allowed to be mounted and to transfer the security data there.  It
      should return 0 or a negative error code.
 
- (*) void security_sb_reconfigure(struct fs_context *fc);
+   * ::
+
+       void security_sb_reconfigure(struct fs_context *fc);
 
      Called to apply any reconfiguration to an LSM's context.  It must not
      fail.  Error checking and resource allocation must be done in advance by
      the parameter parsing and validation hooks.
 
- (*) int security_sb_mountpoint(struct fs_context *fc, struct path *mountpoint,
-                               unsigned int mnt_flags);
+   * ::
+
+       int security_sb_mountpoint(struct fs_context *fc,
+                                  struct path *mountpoint,
+                                  unsigned int mnt_flags);
 
      Called during the mount procedure to verify that the root dentry attached
      to the context is permitted to be attached to the specified mountpoint.
      It should return 0 on success or a negative error code on failure.
 
 
-==========================
-VFS FILESYSTEM CONTEXT API
+VFS Filesystem context API
 ==========================
 
 There are four operations for creating a filesystem context and one for
 destroying a context:
 
- (*) struct fs_context *fs_context_for_mount(
-               struct file_system_type *fs_type,
-               unsigned int sb_flags);
+   * ::
+
+       struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
+                                              unsigned int sb_flags);
 
      Allocate a filesystem context for the purpose of setting up a new mount,
      whether that be with a new superblock or sharing an existing one.  This
@@ -359,7 +416,9 @@ destroying a context:
      fs_type specifies the filesystem type that will manage the context and
      sb_flags presets the superblock flags stored therein.
 
- (*) struct fs_context *fs_context_for_reconfigure(
+   * ::
+
+       struct fs_context *fs_context_for_reconfigure(
                struct dentry *dentry,
                unsigned int sb_flags,
                unsigned int sb_flags_mask);
@@ -369,7 +428,9 @@ destroying a context:
      configured.  sb_flags and sb_flags_mask indicate which superblock flags
      need changing and to what.
 
- (*) struct fs_context *fs_context_for_submount(
+   * ::
+
+       struct fs_context *fs_context_for_submount(
                struct file_system_type *fs_type,
                struct dentry *reference);
 
@@ -382,7 +443,9 @@ destroying a context:
      Note that it's not a requirement that the reference dentry be of the same
      filesystem type as fs_type.
 
- (*) struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc);
+   * ::
+
+        struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc);
 
      Duplicate a filesystem context, copying any options noted and duplicating
      or additionally referencing any resources held therein.  This is available
@@ -392,14 +455,18 @@ destroying a context:
 
      The purpose in the new context is inherited from the old one.
 
- (*) void put_fs_context(struct fs_context *fc);
+   * ::
+
+       void put_fs_context(struct fs_context *fc);
 
      Destroy a filesystem context, releasing any resources it holds.  This
      calls the ->free() operation.  This is intended to be called by anyone who
      created a filesystem context.
 
-     [!] filesystem contexts are not refcounted, so this causes unconditional
-        destruction.
+     .. Warning::
+
+        filesystem contexts are not refcounted, so this causes unconditional
+       destruction.
 
 In all the above operations, apart from the put op, the return is a mount
 context pointer or a negative error code.
@@ -407,8 +474,10 @@ context pointer or a negative error code.
 For the remaining operations, if an error occurs, a negative error code will be
 returned.
 
- (*) int vfs_parse_fs_param(struct fs_context *fc,
-                           struct fs_parameter *param);
+   * ::
+
+        int vfs_parse_fs_param(struct fs_context *fc,
+                              struct fs_parameter *param);
 
      Supply a single mount parameter to the filesystem context.  This include
      the specification of the source/device which is specified as the "source"
@@ -423,53 +492,64 @@ returned.
 
      The parameter value is typed and can be one of:
 
-       fs_value_is_flag,               Parameter not given a value.
-       fs_value_is_string,             Value is a string
-       fs_value_is_blob,               Value is a binary blob
-       fs_value_is_filename,           Value is a filename* + dirfd
-       fs_value_is_file,               Value is an open file (file*)
+       ====================            =============================
+       fs_value_is_flag                Parameter not given a value
+       fs_value_is_string              Value is a string
+       fs_value_is_blob                Value is a binary blob
+       fs_value_is_filename            Value is a filename* + dirfd
+       fs_value_is_file                Value is an open file (file*)
+       ====================            =============================
 
      If there is a value, that value is stored in a union in the struct in one
      of param->{string,blob,name,file}.  Note that the function may steal and
      clear the pointer, but then becomes responsible for disposing of the
      object.
 
- (*) int vfs_parse_fs_string(struct fs_context *fc, const char *key,
-                            const char *value, size_t v_size);
+   * ::
+
+       int vfs_parse_fs_string(struct fs_context *fc, const char *key,
+                              const char *value, size_t v_size);
 
      A wrapper around vfs_parse_fs_param() that copies the value string it is
      passed.
 
- (*) int generic_parse_monolithic(struct fs_context *fc, void *data);
+   * ::
+
+       int generic_parse_monolithic(struct fs_context *fc, void *data);
 
      Parse a sys_mount() data page, assuming the form to be a text list
      consisting of key[=val] options separated by commas.  Each item in the
      list is passed to vfs_mount_option().  This is the default when the
      ->parse_monolithic() method is NULL.
 
- (*) int vfs_get_tree(struct fs_context *fc);
+   * ::
+
+       int vfs_get_tree(struct fs_context *fc);
 
      Get or create the mountable root and superblock, using the parameters in
      the filesystem context to select/configure the superblock.  This invokes
      the ->get_tree() method.
 
- (*) struct vfsmount *vfs_create_mount(struct fs_context *fc);
+   * ::
+
+       struct vfsmount *vfs_create_mount(struct fs_context *fc);
 
      Create a mount given the parameters in the specified filesystem context.
      Note that this does not attach the mount to anything.
 
 
-===========================
-SUPERBLOCK CREATION HELPERS
+Superblock Creation Helpers
 ===========================
 
 A number of VFS helpers are available for use by filesystems for the creation
 or looking up of superblocks.
 
- (*) struct super_block *
-     sget_fc(struct fs_context *fc,
-            int (*test)(struct super_block *sb, struct fs_context *fc),
-            int (*set)(struct super_block *sb, struct fs_context *fc));
+   * ::
+
+       struct super_block *
+       sget_fc(struct fs_context *fc,
+              int (*test)(struct super_block *sb, struct fs_context *fc),
+              int (*set)(struct super_block *sb, struct fs_context *fc));
 
      This is the core routine.  If test is non-NULL, it searches for an
      existing superblock matching the criteria held in the fs_context, using
@@ -482,10 +562,12 @@ or looking up of superblocks.
 
 The following helpers all wrap sget_fc():
 
- (*) int vfs_get_super(struct fs_context *fc,
-                      enum vfs_get_super_keying keying,
-                      int (*fill_super)(struct super_block *sb,
-                                        struct fs_context *fc))
+   * ::
+
+       int vfs_get_super(struct fs_context *fc,
+                        enum vfs_get_super_keying keying,
+                        int (*fill_super)(struct super_block *sb,
+                                          struct fs_context *fc))
 
      This creates/looks up a deviceless superblock.  The keying indicates how
      many superblocks of this type may exist and in what manner they may be
@@ -515,14 +597,14 @@ PARAMETER DESCRIPTION
 =====================
 
 Parameters are described using structures defined in linux/fs_parser.h.
-There's a core description struct that links everything together:
+There's a core description struct that links everything together::
 
        struct fs_parameter_description {
                const struct fs_parameter_spec *specs;
                const struct fs_parameter_enum *enums;
        };
 
-For example:
+For example::
 
        enum {
                Opt_autocell,
@@ -539,10 +621,12 @@ For example:
 
 The members are as follows:
 
- (1) const struct fs_parameter_specification *specs;
+ (1) ::
+
+       const struct fs_parameter_specification *specs;
 
      Table of parameter specifications, terminated with a null entry, where the
-     entries are of type:
+     entries are of type::
 
        struct fs_parameter_spec {
                const char              *name;
@@ -558,6 +642,7 @@ The members are as follows:
 
      The 'type' field indicates the desired value type and must be one of:
 
+       ======================= ======================= =====================
        TYPE NAME               EXPECTED VALUE          RESULT IN
        ======================= ======================= =====================
        fs_param_is_flag        No value                n/a
@@ -573,19 +658,23 @@ The members are as follows:
        fs_param_is_blockdev    Blockdev path           * Needs lookup
        fs_param_is_path        Path                    * Needs lookup
        fs_param_is_fd          File descriptor         result->int_32
+       ======================= ======================= =====================
 
      Note that if the value is of fs_param_is_bool type, fs_parse() will try
      to match any string value against "0", "1", "no", "yes", "false", "true".
 
      Each parameter can also be qualified with 'flags':
 
+       ======================= ================================================
        fs_param_v_optional     The value is optional
        fs_param_neg_with_no    result->negated set if key is prefixed with "no"
        fs_param_neg_with_empty result->negated set if value is ""
        fs_param_deprecated     The parameter is deprecated.
+       ======================= ================================================
 
      These are wrapped with a number of convenience wrappers:
 
+       ======================= ===============================================
        MACRO                   SPECIFIES
        ======================= ===============================================
        fsparam_flag()          fs_param_is_flag
@@ -602,9 +691,10 @@ The members are as follows:
        fsparam_bdev()          fs_param_is_blockdev
        fsparam_path()          fs_param_is_path
        fsparam_fd()            fs_param_is_fd
+       ======================= ===============================================
 
      all of which take two arguments, name string and option number - for
-     example:
+     example::
 
        static const struct fs_parameter_spec afs_param_specs[] = {
                fsparam_flag    ("autocell",    Opt_autocell),
@@ -618,10 +708,12 @@ The members are as follows:
      of arguments to specify the type and the flags for anything that doesn't
      match one of the above macros.
 
- (2) const struct fs_parameter_enum *enums;
+ (2) ::
+
+       const struct fs_parameter_enum *enums;
 
      Table of enum value names to integer mappings, terminated with a null
-     entry.  This is of type:
+     entry.  This is of type::
 
        struct fs_parameter_enum {
                u8              opt;
@@ -630,7 +722,7 @@ The members are as follows:
        };
 
      Where the array is an unsorted list of { parameter ID, name }-keyed
-     elements that indicate the value to map to, e.g.:
+     elements that indicate the value to map to, e.g.::
 
        static const struct fs_parameter_enum afs_param_enums[] = {
                { Opt_bar,   "x",      1},
@@ -648,18 +740,19 @@ CONFIG_VALIDATE_FS_PARSER=y) and will allow the description to be queried from
 userspace using the fsinfo() syscall.
 
 
-==========================
-PARAMETER HELPER FUNCTIONS
+Parameter Helper Functions
 ==========================
 
 A number of helper functions are provided to help a filesystem or an LSM
 process the parameters it is given.
 
- (*) int lookup_constant(const struct constant_table tbl[],
-                        const char *name, int not_found);
+   * ::
+
+       int lookup_constant(const struct constant_table tbl[],
+                          const char *name, int not_found);
 
      Look up a constant by name in a table of name -> integer mappings.  The
-     table is an array of elements of the following type:
+     table is an array of elements of the following type::
 
        struct constant_table {
                const char      *name;
@@ -669,9 +762,11 @@ process the parameters it is given.
      If a match is found, the corresponding value is returned.  If a match
      isn't found, the not_found value is returned instead.
 
- (*) bool validate_constant_table(const struct constant_table *tbl,
-                                 size_t tbl_size,
-                                 int low, int high, int special);
+   * ::
+
+       bool validate_constant_table(const struct constant_table *tbl,
+                                   size_t tbl_size,
+                                   int low, int high, int special);
 
      Validate a constant table.  Checks that all the elements are appropriately
      ordered, that there are no duplicates and that the values are between low
@@ -682,16 +777,20 @@ process the parameters it is given.
      If all is good, true is returned.  If the table is invalid, errors are
      logged to dmesg and false is returned.
 
- (*) bool fs_validate_description(const struct fs_parameter_description *desc);
+   * ::
+
+       bool fs_validate_description(const struct fs_parameter_description *desc);
 
      This performs some validation checks on a parameter description.  It
      returns true if the description is good and false if it is not.  It will
      log errors to dmesg if validation fails.
 
- (*) int fs_parse(struct fs_context *fc,
-                 const struct fs_parameter_description *desc,
-                 struct fs_parameter *param,
-                 struct fs_parse_result *result);
+   * ::
+
+        int fs_parse(struct fs_context *fc,
+                    const struct fs_parameter_description *desc,
+                    struct fs_parameter *param,
+                    struct fs_parse_result *result);
 
      This is the main interpreter of parameters.  It uses the parameter
      description to look up a parameter by key name and to convert that to an
@@ -711,14 +810,16 @@ process the parameters it is given.
      parameter is matched, but the value is erroneous, -EINVAL will be
      returned; otherwise the parameter's option number will be returned.
 
- (*) int fs_lookup_param(struct fs_context *fc,
-                        struct fs_parameter *value,
-                        bool want_bdev,
-                        struct path *_path);
+   * ::
+
+       int fs_lookup_param(struct fs_context *fc,
+                          struct fs_parameter *value,
+                          bool want_bdev,
+                          struct path *_path);
 
      This takes a parameter that carries a string or filename type and attempts
      to do a path lookup on it.  If the parameter expects a blockdev, a check
      is made that the inode actually represents one.
 
-     Returns 0 if successful and *_path will be set; returns a negative error
-     code if not.
+     Returns 0 if successful and ``*_path`` will be set; returns a negative
+     error code if not.
index e41369709c5b0359a33a10ebaedc5efa838c6c53..463e37694250ada04c5bfeeaa798525f1a2e5325 100644 (file)
@@ -119,9 +119,7 @@ it comes to that question::
 
     /opt/ofs/bin/pvfs2-genconfig /etc/pvfs2.conf
 
-Create an /etc/pvfs2tab file::
-
-Localhost is fine for your pvfs2tab file:
+Create an /etc/pvfs2tab file (localhost is fine)::
 
     echo tcp://localhost:3334/orangefs /pvfsmnt pvfs2 defaults,noauto 0 0 > \
        /etc/pvfs2tab
index 38b606991065b3df4f075bb3120dc9e5db09bf71..430963e0e8c34b6bd441629b45104b36860260a4 100644 (file)
@@ -543,6 +543,7 @@ encoded manner. The codes are the following:
     hg    huge page advise flag
     nh    no huge page advise flag
     mg    mergable advise flag
+    bt  - arm64 BTI guarded page
     ==    =======================================
 
 Note that there is no guarantee that every flag and associated mnemonic will
@@ -1042,8 +1043,8 @@ PageTables
               amount of memory dedicated to the lowest level of page
               tables.
 NFS_Unstable
-              NFS pages sent to the server, but not yet committed to stable
-             storage
+              Always zero. Previous counted pages which had been written to
+              the server, but has not been committed to stable storage.
 Bounce
               Memory used for block device "bounce buffers"
 WritebackTmp
@@ -1870,7 +1871,7 @@ unbindable        mount is unbindable
 
 For more information on mount propagation see:
 
-  Documentation/filesystems/sharedsubtree.txt
+  Documentation/filesystems/sharedsubtree.rst
 
 
 3.6    /proc/<pid>/comm  & /proc/<pid>/task/<tid>/comm
similarity index 81%
rename from Documentation/filesystems/quota.txt
rename to Documentation/filesystems/quota.rst
index 32874b06ebe917b2a17ae492a6c140645ab8167b..a30cdd47c652a4480b75c5af8ab2194a4be0d415 100644 (file)
@@ -1,4 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
 
+===============
 Quota subsystem
 ===============
 
@@ -39,6 +41,7 @@ Currently, the interface supports only one message type QUOTA_NL_C_WARNING.
 This command is used to send a notification about any of the above mentioned
 events. Each message has six attributes. These are (type of the argument is
 in parentheses):
+
         QUOTA_NL_A_QTYPE (u32)
          - type of quota being exceeded (one of USRQUOTA, GRPQUOTA)
         QUOTA_NL_A_EXCESS_ID (u64)
@@ -48,20 +51,34 @@ in parentheses):
          - UID of a user who caused the event
         QUOTA_NL_A_WARNING (u32)
          - what kind of limit is exceeded:
-               QUOTA_NL_IHARDWARN - inode hardlimit
-               QUOTA_NL_ISOFTLONGWARN - inode softlimit is exceeded longer
-                 than given grace period
-               QUOTA_NL_ISOFTWARN - inode softlimit
-               QUOTA_NL_BHARDWARN - space (block) hardlimit
-               QUOTA_NL_BSOFTLONGWARN - space (block) softlimit is exceeded
-                 longer than given grace period.
-               QUOTA_NL_BSOFTWARN - space (block) softlimit
+
+               QUOTA_NL_IHARDWARN
+                   inode hardlimit
+               QUOTA_NL_ISOFTLONGWARN
+                   inode softlimit is exceeded longer
+                   than given grace period
+               QUOTA_NL_ISOFTWARN
+                   inode softlimit
+               QUOTA_NL_BHARDWARN
+                   space (block) hardlimit
+               QUOTA_NL_BSOFTLONGWARN
+                   space (block) softlimit is exceeded
+                   longer than given grace period.
+               QUOTA_NL_BSOFTWARN
+                   space (block) softlimit
+
          - four warnings are also defined for the event when user stops
            exceeding some limit:
-               QUOTA_NL_IHARDBELOW - inode hardlimit
-               QUOTA_NL_ISOFTBELOW - inode softlimit
-               QUOTA_NL_BHARDBELOW - space (block) hardlimit
-               QUOTA_NL_BSOFTBELOW - space (block) softlimit
+
+               QUOTA_NL_IHARDBELOW
+                   inode hardlimit
+               QUOTA_NL_ISOFTBELOW
+                   inode softlimit
+               QUOTA_NL_BHARDBELOW
+                   space (block) hardlimit
+               QUOTA_NL_BSOFTBELOW
+                   space (block) softlimit
+
         QUOTA_NL_A_DEV_MAJOR (u32)
          - major number of a device with the affected filesystem
         QUOTA_NL_A_DEV_MINOR (u32)
index 6c576e241d864d530f9534f0b597050d62e1f2b3..3fddacc6bf14979f330fcd0f08f3fa11d0e2656e 100644 (file)
@@ -71,7 +71,7 @@ be allowed write access to a ramfs mount.
 
 A ramfs derivative called tmpfs was created to add size limits, and the ability
 to write the data to swap space.  Normal users can be allowed write access to
-tmpfs mounts.  See Documentation/filesystems/tmpfs.txt for more information.
+tmpfs mounts.  See Documentation/filesystems/tmpfs.rst for more information.
 
 What is rootfs?
 ---------------
similarity index 92%
rename from Documentation/filesystems/seq_file.txt
rename to Documentation/filesystems/seq_file.rst
index d412b236a9d6fa81e0adf1acb68e265c8e71bd14..fab302046b13c0438187bcf3a3a92f84482eed5e 100644 (file)
@@ -1,6 +1,11 @@
-The seq_file interface
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+The seq_file Interface
+======================
 
        Copyright 2003 Jonathan Corbet <corbet@lwn.net>
+
        This file is originally from the LWN.net Driver Porting series at
        http://lwn.net/Articles/driver-porting/
 
@@ -43,7 +48,7 @@ loadable module which creates a file called /proc/sequence. The file, when
 read, simply produces a set of increasing integer values, one per line. The
 sequence will continue until the user loses patience and finds something
 better to do. The file is seekable, in that one can do something like the
-following:
+following::
 
     dd if=/proc/sequence of=out1 count=1
     dd if=/proc/sequence skip=1 of=out2 count=1
@@ -55,16 +60,18 @@ wanting to see the full source for this module can find it at
 http://lwn.net/Articles/22359/).
 
 Deprecated create_proc_entry
+============================
 
 Note that the above article uses create_proc_entry which was removed in
-kernel 3.10. Current versions require the following update
+kernel 3.10. Current versions require the following update::
 
--      entry = create_proc_entry("sequence", 0, NULL);
--      if (entry)
--              entry->proc_fops = &ct_file_ops;
-+      entry = proc_create("sequence", 0, NULL, &ct_file_ops);
+    -  entry = create_proc_entry("sequence", 0, NULL);
+    -  if (entry)
+    -          entry->proc_fops = &ct_file_ops;
+    +  entry = proc_create("sequence", 0, NULL, &ct_file_ops);
 
 The iterator interface
+======================
 
 Modules implementing a virtual file with seq_file must implement an
 iterator object that allows stepping through the data of interest
@@ -99,7 +106,7 @@ position.  The pos passed to start() will always be either zero, or
 the most recent pos used in the previous session.
 
 For our simple sequence example,
-the start() function looks like:
+the start() function looks like::
 
        static void *ct_seq_start(struct seq_file *s, loff_t *pos)
        {
@@ -129,7 +136,7 @@ move the iterator forward to the next position in the sequence.  The
 example module can simply increment the position by one; more useful
 modules will do what is needed to step through some data structure. The
 next() function returns a new iterator, or NULL if the sequence is
-complete. Here's the example version:
+complete. Here's the example version::
 
        static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
        {
@@ -141,10 +148,10 @@ complete. Here's the example version:
 The stop() function closes a session; its job, of course, is to clean
 up. If dynamic memory is allocated for the iterator, stop() is the
 place to free it; if a lock was taken by start(), stop() must release
-that lock.  The value that *pos was set to by the last next() call
+that lock.  The value that ``*pos`` was set to by the last next() call
 before stop() is remembered, and used for the first start() call of
 the next session unless lseek() has been called on the file; in that
-case next start() will be asked to start at position zero.
+case next start() will be asked to start at position zero::
 
        static void ct_seq_stop(struct seq_file *s, void *v)
        {
@@ -152,7 +159,7 @@ case next start() will be asked to start at position zero.
        }
 
 Finally, the show() function should format the object currently pointed to
-by the iterator for output.  The example module's show() function is:
+by the iterator for output.  The example module's show() function is::
 
        static int ct_seq_show(struct seq_file *s, void *v)
        {
@@ -169,7 +176,7 @@ generated output before returning SEQ_SKIP, that output will be dropped.
 
 We will look at seq_printf() in a moment. But first, the definition of the
 seq_file iterator is finished by creating a seq_operations structure with
-the four functions we have just defined:
+the four functions we have just defined::
 
        static const struct seq_operations ct_seq_ops = {
                .start = ct_seq_start,
@@ -194,6 +201,7 @@ other locks while the iterator is active.
 
 
 Formatted output
+================
 
 The seq_file code manages positioning within the output created by the
 iterator and getting it into the user's buffer. But, for that to work, that
@@ -203,7 +211,7 @@ been defined which make this task easy.
 Most code will simply use seq_printf(), which works pretty much like
 printk(), but which requires the seq_file pointer as an argument.
 
-For straight character output, the following functions may be used:
+For straight character output, the following functions may be used::
 
        seq_putc(struct seq_file *m, char c);
        seq_puts(struct seq_file *m, const char *s);
@@ -213,7 +221,7 @@ The first two output a single character and a string, just like one would
 expect. seq_escape() is like seq_puts(), except that any character in s
 which is in the string esc will be represented in octal form in the output.
 
-There are also a pair of functions for printing filenames:
+There are also a pair of functions for printing filenames::
 
        int seq_path(struct seq_file *m, const struct path *path,
                     const char *esc);
@@ -226,8 +234,10 @@ the path relative to the current process's filesystem root.  If a different
 root is desired, it can be used with seq_path_root().  If it turns out that
 path cannot be reached from root, seq_path_root() returns SEQ_SKIP.
 
-A function producing complicated output may want to check
+A function producing complicated output may want to check::
+
        bool seq_has_overflowed(struct seq_file *m);
+
 and avoid further seq_<output> calls if true is returned.
 
 A true return from seq_has_overflowed means that the seq_file buffer will
@@ -236,6 +246,7 @@ buffer and retry printing.
 
 
 Making it all work
+==================
 
 So far, we have a nice set of functions which can produce output within the
 seq_file system, but we have not yet turned them into a file that a user
@@ -244,7 +255,7 @@ creation of a set of file_operations which implement the operations on that
 file. The seq_file interface provides a set of canned operations which do
 most of the work. The virtual file author still must implement the open()
 method, however, to hook everything up. The open function is often a single
-line, as in the example module:
+line, as in the example module::
 
        static int ct_open(struct inode *inode, struct file *file)
        {
@@ -263,7 +274,7 @@ by the iterator functions.
 There is also a wrapper function to seq_open() called seq_open_private(). It
 kmallocs a zero filled block of memory and stores a pointer to it in the
 private field of the seq_file structure, returning 0 on success. The
-block size is specified in a third parameter to the function, e.g.:
+block size is specified in a third parameter to the function, e.g.::
 
        static int ct_open(struct inode *inode, struct file *file)
        {
@@ -273,7 +284,7 @@ block size is specified in a third parameter to the function, e.g.:
 
 There is also a variant function, __seq_open_private(), which is functionally
 identical except that, if successful, it returns the pointer to the allocated
-memory block, allowing further initialisation e.g.:
+memory block, allowing further initialisation e.g.::
 
        static int ct_open(struct inode *inode, struct file *file)
        {
@@ -295,7 +306,7 @@ frees the memory allocated in the corresponding open.
 
 The other operations of interest - read(), llseek(), and release() - are
 all implemented by the seq_file code itself. So a virtual file's
-file_operations structure will look like:
+file_operations structure will look like::
 
        static const struct file_operations ct_file_ops = {
                .owner   = THIS_MODULE,
@@ -309,7 +320,7 @@ There is also a seq_release_private() which passes the contents of the
 seq_file private field to kfree() before releasing the structure.
 
 The final step is the creation of the /proc file itself. In the example
-code, that is done in the initialization code in the usual way:
+code, that is done in the initialization code in the usual way::
 
        static int ct_init(void)
        {
@@ -325,9 +336,10 @@ And that is pretty much it.
 
 
 seq_list
+========
 
 If your file will be iterating through a linked list, you may find these
-routines useful:
+routines useful::
 
        struct list_head *seq_list_start(struct list_head *head,
                                         loff_t pos);
@@ -338,15 +350,16 @@ routines useful:
 
 These helpers will interpret pos as a position within the list and iterate
 accordingly.  Your start() and next() functions need only invoke the
-seq_list_* helpers with a pointer to the appropriate list_head structure.
+``seq_list_*`` helpers with a pointer to the appropriate list_head structure.
 
 
 The extra-simple version
+========================
 
 For extremely simple virtual files, there is an even easier interface.  A
 module can define only the show() function, which should create all the
 output that the virtual file will contain. The file's open() method then
-calls:
+calls::
 
        int single_open(struct file *file,
                        int (*show)(struct seq_file *m, void *p),
similarity index 72%
rename from Documentation/filesystems/sharedsubtree.txt
rename to Documentation/filesystems/sharedsubtree.rst
index 8ccfbd55244b4dc837665153bf74e8bdecb9a2b1..d83395354250d9ca02dc19a38cdf9762733ded04 100644 (file)
@@ -1,7 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
 Shared Subtrees
----------------
+===============
 
-Contents:
+.. Contents:
        1) Overview
        2) Features
        3) Setting mount states
@@ -41,31 +44,38 @@ replicas continue to be exactly same.
 
        Here is an example:
 
-       Let's say /mnt has a mount that is shared.
-       mount --make-shared /mnt
+       Let's say /mnt has a mount that is shared::
+
+           mount --make-shared /mnt
 
        Note: mount(8) command now supports the --make-shared flag,
        so the sample 'smount' program is no longer needed and has been
        removed.
 
-       # mount --bind /mnt /tmp
+       ::
+
+           # mount --bind /mnt /tmp
+
        The above command replicates the mount at /mnt to the mountpoint /tmp
        and the contents of both the mounts remain identical.
 
-       #ls /mnt
-       a b c
+       ::
 
-       #ls /tmp
-       a b c
+           #ls /mnt
+           a b c
 
-       Now let's say we mount a device at /tmp/a
-       # mount /dev/sd0  /tmp/a
+           #ls /tmp
+           a b c
 
-       #ls /tmp/a
-       t1 t2 t3
+       Now let's say we mount a device at /tmp/a::
 
-       #ls /mnt/a
-       t1 t2 t3
+           # mount /dev/sd0  /tmp/a
+
+           #ls /tmp/a
+           t1 t2 t3
+
+           #ls /mnt/a
+           t1 t2 t3
 
        Note that the mount has propagated to the mount at /mnt as well.
 
@@ -123,14 +133,15 @@ replicas continue to be exactly same.
 
 2d) A unbindable mount is a unbindable private mount
 
-       let's say we have a mount at /mnt and we make it unbindable
+       let's say we have a mount at /mnt and we make it unbindable::
+
+           # mount --make-unbindable /mnt
 
-       # mount --make-unbindable /mnt
+        Let's try to bind mount this mount somewhere else::
 
-        Let's try to bind mount this mount somewhere else.
-        # mount --bind /mnt /tmp
-        mount: wrong fs type, bad option, bad superblock on /mnt,
-               or too many mounted file systems
+           # mount --bind /mnt /tmp
+           mount: wrong fs type, bad option, bad superblock on /mnt,
+                   or too many mounted file systems
 
        Binding a unbindable mount is a invalid operation.
 
@@ -138,12 +149,12 @@ replicas continue to be exactly same.
 3) Setting mount states
 
        The mount command (util-linux package) can be used to set mount
-       states:
+       states::
 
-       mount --make-shared mountpoint
-       mount --make-slave mountpoint
-       mount --make-private mountpoint
-       mount --make-unbindable mountpoint
+           mount --make-shared mountpoint
+           mount --make-slave mountpoint
+           mount --make-private mountpoint
+           mount --make-unbindable mountpoint
 
 
 4) Use cases
@@ -154,9 +165,10 @@ replicas continue to be exactly same.
 
           Solution:
 
-               The system administrator can make the mount at /cdrom shared
-               mount --bind /cdrom /cdrom
-               mount --make-shared /cdrom
+               The system administrator can make the mount at /cdrom shared::
+
+                   mount --bind /cdrom /cdrom
+                   mount --make-shared /cdrom
 
                Now any process that clones off a new namespace will have a
                mount at /cdrom which is a replica of the same mount in the
@@ -172,14 +184,14 @@ replicas continue to be exactly same.
           Solution:
 
                To begin with, the administrator can mark the entire mount tree
-               as shareable.
+               as shareable::
 
-               mount --make-rshared /
+                   mount --make-rshared /
 
                A new process can clone off a new namespace. And mark some part
-               of its namespace as slave
+               of its namespace as slave::
 
-               mount --make-rslave /myprivatetree
+                   mount --make-rslave /myprivatetree
 
                Hence forth any mounts within the /myprivatetree done by the
                process will not show up in any other namespace. However mounts
@@ -206,13 +218,13 @@ replicas continue to be exactly same.
                versions of the file depending on the path used to access that
                file.
 
-               An example is:
+               An example is::
 
-               mount --make-shared /
-               mount --rbind / /view/v1
-               mount --rbind / /view/v2
-               mount --rbind / /view/v3
-               mount --rbind / /view/v4
+                   mount --make-shared /
+                   mount --rbind / /view/v1
+                   mount --rbind / /view/v2
+                   mount --rbind / /view/v3
+                   mount --rbind / /view/v4
 
                and if /usr has a versioning filesystem mounted, then that
                mount appears at /view/v1/usr, /view/v2/usr, /view/v3/usr and
@@ -224,8 +236,8 @@ replicas continue to be exactly same.
                filesystem is being requested and return the corresponding
                inode.
 
-5) Detailed semantics:
--------------------
+5) Detailed semantics
+---------------------
        The section below explains the detailed semantics of
        bind, rbind, move, mount, umount and clone-namespace operations.
 
@@ -235,6 +247,7 @@ replicas continue to be exactly same.
 5a) Mount states
 
        A given mount can be in one of the following states
+
        1) shared
        2) slave
        3) shared and slave
@@ -252,7 +265,8 @@ replicas continue to be exactly same.
                A 'shared mount' is defined as a vfsmount that belongs to a
                'peer group'.
 
-               For example:
+               For example::
+
                        mount --make-shared /mnt
                        mount --bind /mnt /tmp
 
@@ -270,7 +284,7 @@ replicas continue to be exactly same.
                A slave mount as the name implies has a master mount from which
                mount/unmount events are received. Events do not propagate from
                the slave mount to the master.  Only a shared mount can be made
-               a slave by executing the following command
+               a slave by executing the following command::
 
                        mount --make-slave mount
 
@@ -290,8 +304,10 @@ replicas continue to be exactly same.
                peer group.
 
                Only a slave vfsmount can be made as 'shared and slave' by
-               either executing the following command
+               either executing the following command::
+
                        mount --make-shared mount
+
                or by moving the slave vfsmount under a shared vfsmount.
 
        (4) Private mount
@@ -307,30 +323,32 @@ replicas continue to be exactly same.
 
 
        State diagram:
+
        The state diagram below explains the state transition of a mount,
-       in response to various commands.
-       ------------------------------------------------------------------------
-       |             |make-shared |  make-slave  | make-private |make-unbindab|
-       --------------|------------|--------------|--------------|-------------|
-       |shared       |shared      |*slave/private|   private    | unbindable  |
-       |             |            |              |              |             |
-       |-------------|------------|--------------|--------------|-------------|
-       |slave        |shared      |    **slave   |    private   | unbindable  |
-       |             |and slave   |              |              |             |
-       |-------------|------------|--------------|--------------|-------------|
-       |shared       |shared      |    slave     |    private   | unbindable  |
-       |and slave    |and slave   |              |              |             |
-       |-------------|------------|--------------|--------------|-------------|
-       |private      |shared      |  **private   |    private   | unbindable  |
-       |-------------|------------|--------------|--------------|-------------|
-       |unbindable   |shared      |**unbindable  |    private   | unbindable  |
-       ------------------------------------------------------------------------
-
-       * if the shared mount is the only mount in its peer group, making it
-       slave, makes it private automatically. Note that there is no master to
-       which it can be slaved to.
-
-       ** slaving a non-shared mount has no effect on the mount.
+       in response to various commands::
+
+           -----------------------------------------------------------------------
+           |             |make-shared |  make-slave  | make-private |make-unbindab|
+           --------------|------------|--------------|--------------|-------------|
+           |shared       |shared      |*slave/private|   private    | unbindable  |
+           |             |            |              |              |             |
+           |-------------|------------|--------------|--------------|-------------|
+           |slave        |shared      | **slave      |    private   | unbindable  |
+           |             |and slave   |              |              |             |
+           |-------------|------------|--------------|--------------|-------------|
+           |shared       |shared      | slave        |    private   | unbindable  |
+           |and slave    |and slave   |              |              |             |
+           |-------------|------------|--------------|--------------|-------------|
+           |private      |shared      |  **private   |    private   | unbindable  |
+           |-------------|------------|--------------|--------------|-------------|
+           |unbindable   |shared      |**unbindable  |    private   | unbindable  |
+           ------------------------------------------------------------------------
+
+           * if the shared mount is the only mount in its peer group, making it
+           slave, makes it private automatically. Note that there is no master to
+           which it can be slaved to.
+
+           ** slaving a non-shared mount has no effect on the mount.
 
        Apart from the commands listed below, the 'move' operation also changes
        the state of a mount depending on type of the destination mount. Its
@@ -338,31 +356,32 @@ replicas continue to be exactly same.
 
 5b) Bind semantics
 
-       Consider the following command
+       Consider the following command::
 
-       mount --bind A/a  B/b
+           mount --bind A/a  B/b
 
        where 'A' is the source mount, 'a' is the dentry in the mount 'A', 'B'
        is the destination mount and 'b' is the dentry in the destination mount.
 
        The outcome depends on the type of mount of 'A' and 'B'. The table
-       below contains quick reference.
-   ---------------------------------------------------------------------------
-   |         BIND MOUNT OPERATION                                            |
-   |**************************************************************************
-   |source(A)->| shared       |       private  |       slave    | unbindable |
-   | dest(B)  |               |                |                |            |
-   |   |      |               |                |                |            |
-   |   v      |               |                |                |            |
-   |**************************************************************************
-   |  shared  | shared        |     shared     | shared & slave |  invalid   |
-   |          |               |                |                |            |
-   |non-shared| shared        |      private   |      slave     |  invalid   |
-   ***************************************************************************
+       below contains quick reference::
+
+           --------------------------------------------------------------------------
+           |         BIND MOUNT OPERATION                                           |
+           |************************************************************************|
+           |source(A)->| shared      |       private  |       slave    | unbindable |
+           | dest(B)  |              |                |                |            |
+           |   |      |              |                |                |            |
+           |   v      |              |                |                |            |
+           |************************************************************************|
+           |  shared  | shared       |     shared     | shared & slave |  invalid   |
+           |          |              |                |                |            |
+           |non-shared| shared       |      private   |      slave     |  invalid   |
+           **************************************************************************
 
        Details:
 
-       1. 'A' is a shared mount and 'B' is a shared mount. A new mount 'C'
+    1. 'A' is a shared mount and 'B' is a shared mount. A new mount 'C'
        which is clone of 'A', is created. Its root dentry is 'a' . 'C' is
        mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ...
        are created and mounted at the dentry 'b' on all mounts where 'B'
@@ -371,7 +390,7 @@ replicas continue to be exactly same.
        'B'.  And finally the peer-group of 'C' is merged with the peer group
        of 'A'.
 
-       2. 'A' is a private mount and 'B' is a shared mount. A new mount 'C'
+    2. 'A' is a private mount and 'B' is a shared mount. A new mount 'C'
        which is clone of 'A', is created. Its root dentry is 'a'. 'C' is
        mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ...
        are created and mounted at the dentry 'b' on all mounts where 'B'
@@ -379,7 +398,7 @@ replicas continue to be exactly same.
        'C', 'C1', .., 'Cn' with exactly the same configuration as the
        propagation tree for 'B'.
 
-       3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. A new
+    3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. A new
        mount 'C' which is clone of 'A', is created. Its root dentry is 'a' .
        'C' is mounted on mount 'B' at dentry 'b'. Also new mounts 'C1', 'C2',
        'C3' ... are created and mounted at the dentry 'b' on all mounts where
@@ -389,19 +408,19 @@ replicas continue to be exactly same.
        is made the slave of mount 'Z'.  In other words, mount 'C' is in the
        state 'slave and shared'.
 
-       4. 'A' is a unbindable mount and 'B' is a shared mount. This is a
+    4. 'A' is a unbindable mount and 'B' is a shared mount. This is a
        invalid operation.
 
-       5. 'A' is a private mount and 'B' is a non-shared(private or slave or
+    5. 'A' is a private mount and 'B' is a non-shared(private or slave or
        unbindable) mount. A new mount 'C' which is clone of 'A', is created.
        Its root dentry is 'a'. 'C' is mounted on mount 'B' at dentry 'b'.
 
-       6. 'A' is a shared mount and 'B' is a non-shared mount. A new mount 'C'
+    6. 'A' is a shared mount and 'B' is a non-shared mount. A new mount 'C'
        which is a clone of 'A' is created. Its root dentry is 'a'. 'C' is
        mounted on mount 'B' at dentry 'b'.  'C' is made a member of the
        peer-group of 'A'.
 
-       7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. A
+    7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. A
        new mount 'C' which is a clone of 'A' is created. Its root dentry is
        'a'.  'C' is mounted on mount 'B' at dentry 'b'. Also 'C' is set as a
        slave mount of 'Z'. In other words 'A' and 'C' are both slave mounts of
@@ -409,7 +428,7 @@ replicas continue to be exactly same.
        mount/unmount on 'A' do not propagate anywhere else. Similarly
        mount/unmount on 'C' do not propagate anywhere else.
 
-       8. 'A' is a unbindable mount and 'B' is a non-shared mount. This is a
+    8. 'A' is a unbindable mount and 'B' is a non-shared mount. This is a
        invalid operation. A unbindable mount cannot be bind mounted.
 
 5c) Rbind semantics
@@ -422,7 +441,9 @@ replicas continue to be exactly same.
        then the subtree under the unbindable mount is pruned in the new
        location.
 
-       eg: let's say we have the following mount tree.
+       eg:
+
+         let's say we have the following mount tree::
 
                A
              /   \
@@ -430,12 +451,12 @@ replicas continue to be exactly same.
             / \ / \
             D E F G
 
-            Let's say all the mount except the mount C in the tree are
-            of a type other than unbindable.
+         Let's say all the mount except the mount C in the tree are
+         of a type other than unbindable.
 
-            If this tree is rbound to say Z
+         If this tree is rbound to say Z
 
-            We will have the following tree at the new location.
+         We will have the following tree at the new location::
 
                Z
                |
@@ -457,24 +478,26 @@ replicas continue to be exactly same.
        the dentry in the destination mount.
 
        The outcome depends on the type of the mount of 'A' and 'B'. The table
-       below is a quick reference.
-   ---------------------------------------------------------------------------
-   |                   MOVE MOUNT OPERATION                                 |
-   |**************************************************************************
-   | source(A)->| shared      |       private  |       slave    | unbindable |
-   | dest(B)  |               |                |                |            |
-   |   |      |               |                |                |            |
-   |   v      |               |                |                |            |
-   |**************************************************************************
-   |  shared  | shared        |     shared     |shared and slave|  invalid   |
-   |          |               |                |                |            |
-   |non-shared| shared        |      private   |    slave       | unbindable |
-   ***************************************************************************
-       NOTE: moving a mount residing under a shared mount is invalid.
+       below is a quick reference::
+
+           ---------------------------------------------------------------------------
+           |                   MOVE MOUNT OPERATION                                 |
+           |**************************************************************************
+           | source(A)->| shared      |       private  |       slave    | unbindable |
+           | dest(B)  |               |                |                |            |
+           |   |      |               |                |                |            |
+           |   v      |               |                |                |            |
+           |**************************************************************************
+           |  shared  | shared        |     shared     |shared and slave|  invalid   |
+           |          |               |                |                |            |
+           |non-shared| shared        |      private   |    slave       | unbindable |
+           ***************************************************************************
+
+       .. Note:: moving a mount residing under a shared mount is invalid.
 
       Details follow:
 
-       1. 'A' is a shared mount and 'B' is a shared mount.  The mount 'A' is
+    1. 'A' is a shared mount and 'B' is a shared mount.  The mount 'A' is
        mounted on mount 'B' at dentry 'b'.  Also new mounts 'A1', 'A2'...'An'
        are created and mounted at dentry 'b' on all mounts that receive
        propagation from mount 'B'. A new propagation tree is created in the
@@ -483,7 +506,7 @@ replicas continue to be exactly same.
        propagation tree is appended to the already existing propagation tree
        of 'A'.
 
-       2. 'A' is a private mount and 'B' is a shared mount. The mount 'A' is
+    2. 'A' is a private mount and 'B' is a shared mount. The mount 'A' is
        mounted on mount 'B' at dentry 'b'. Also new mount 'A1', 'A2'... 'An'
        are created and mounted at dentry 'b' on all mounts that receive
        propagation from mount 'B'. The mount 'A' becomes a shared mount and a
@@ -491,7 +514,7 @@ replicas continue to be exactly same.
        'B'. This new propagation tree contains all the new mounts 'A1',
        'A2'...  'An'.
 
-       3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount.  The
+    3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount.  The
        mount 'A' is mounted on mount 'B' at dentry 'b'.  Also new mounts 'A1',
        'A2'... 'An' are created and mounted at dentry 'b' on all mounts that
        receive propagation from mount 'B'. A new propagation tree is created
@@ -501,32 +524,32 @@ replicas continue to be exactly same.
        'A'.  Mount 'A' continues to be the slave mount of 'Z' but it also
        becomes 'shared'.
 
-       4. 'A' is a unbindable mount and 'B' is a shared mount. The operation
+    4. 'A' is a unbindable mount and 'B' is a shared mount. The operation
        is invalid. Because mounting anything on the shared mount 'B' can
        create new mounts that get mounted on the mounts that receive
        propagation from 'B'.  And since the mount 'A' is unbindable, cloning
        it to mount at other mountpoints is not possible.
 
-       5. 'A' is a private mount and 'B' is a non-shared(private or slave or
+    5. 'A' is a private mount and 'B' is a non-shared(private or slave or
        unbindable) mount. The mount 'A' is mounted on mount 'B' at dentry 'b'.
 
-       6. 'A' is a shared mount and 'B' is a non-shared mount.  The mount 'A'
+    6. 'A' is a shared mount and 'B' is a non-shared mount.  The mount 'A'
        is mounted on mount 'B' at dentry 'b'.  Mount 'A' continues to be a
        shared mount.
 
-       7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount.
+    7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount.
        The mount 'A' is mounted on mount 'B' at dentry 'b'.  Mount 'A'
        continues to be a slave mount of mount 'Z'.
 
-       8. 'A' is a unbindable mount and 'B' is a non-shared mount. The mount
+    8. 'A' is a unbindable mount and 'B' is a non-shared mount. The mount
        'A' is mounted on mount 'B' at dentry 'b'. Mount 'A' continues to be a
        unbindable mount.
 
 5e) Mount semantics
 
-       Consider the following command
+       Consider the following command::
 
-       mount device  B/b
+           mount device  B/b
 
        'B' is the destination mount and 'b' is the dentry in the destination
        mount.
@@ -537,9 +560,9 @@ replicas continue to be exactly same.
 
 5f) Unmount semantics
 
-       Consider the following command
+       Consider the following command::
 
-       umount A
+           umount A
 
        where 'A' is a mount mounted on mount 'B' at dentry 'b'.
 
@@ -592,10 +615,12 @@ replicas continue to be exactly same.
 
        A. What is the result of the following command sequence?
 
-               mount --bind /mnt /mnt
-               mount --make-shared /mnt
-               mount --bind /mnt /tmp
-               mount --move /tmp /mnt/1
+               ::
+
+                   mount --bind /mnt /mnt
+                   mount --make-shared /mnt
+                   mount --bind /mnt /tmp
+                   mount --move /tmp /mnt/1
 
                what should be the contents of /mnt /mnt/1 /mnt/1/1 should be?
                Should they all be identical? or should /mnt and /mnt/1 be
@@ -604,23 +629,27 @@ replicas continue to be exactly same.
 
        B. What is the result of the following command sequence?
 
-               mount --make-rshared /
-               mkdir -p /v/1
-               mount --rbind / /v/1
+               ::
+
+                   mount --make-rshared /
+                   mkdir -p /v/1
+                   mount --rbind / /v/1
 
                what should be the content of /v/1/v/1 be?
 
 
        C. What is the result of the following command sequence?
 
-               mount --bind /mnt /mnt
-               mount --make-shared /mnt
-               mkdir -p /mnt/1/2/3 /mnt/1/test
-               mount --bind /mnt/1 /tmp
-               mount --make-slave /mnt
-               mount --make-shared /mnt
-               mount --bind /mnt/1/2 /tmp1
-               mount --make-slave /mnt
+               ::
+
+                   mount --bind /mnt /mnt
+                   mount --make-shared /mnt
+                   mkdir -p /mnt/1/2/3 /mnt/1/test
+                   mount --bind /mnt/1 /tmp
+                   mount --make-slave /mnt
+                   mount --make-shared /mnt
+                   mount --bind /mnt/1/2 /tmp1
+                   mount --make-slave /mnt
 
                At this point we have the first mount at /tmp and
                its root dentry is 1. Let's call this mount 'A'
@@ -668,7 +697,8 @@ replicas continue to be exactly same.
 
                step 1:
                   let's say the root tree has just two directories with
-                  one vfsmount.
+                  one vfsmount::
+
                                    root
                                   /    \
                                  tmp    usr
@@ -676,14 +706,17 @@ replicas continue to be exactly same.
                    And we want to replicate the tree at multiple
                    mountpoints under /root/tmp
 
-               step2:
-                     mount --make-shared /root
+               step 2:
+                     ::
 
-                     mkdir -p /tmp/m1
 
-                     mount --rbind /root /tmp/m1
+                       mount --make-shared /root
 
-                     the new tree now looks like this:
+                       mkdir -p /tmp/m1
+
+                       mount --rbind /root /tmp/m1
+
+                     the new tree now looks like this::
 
                                    root
                                   /    \
@@ -697,11 +730,13 @@ replicas continue to be exactly same.
 
                          it has two vfsmounts
 
-               step3:
+               step 3:
+                   ::
+
                            mkdir -p /tmp/m2
                            mount --rbind /root /tmp/m2
 
-                       the new tree now looks like this:
+                       the new tree now looks like this::
 
                                      root
                                     /    \
@@ -724,6 +759,7 @@ replicas continue to be exactly same.
                       it has 6 vfsmounts
 
                step 4:
+                     ::
                          mkdir -p /tmp/m3
                          mount --rbind /root /tmp/m3
 
@@ -740,7 +776,8 @@ replicas continue to be exactly same.
 
                step 1:
                   let's say the root tree has just two directories with
-                  one vfsmount.
+                  one vfsmount::
+
                                    root
                                   /    \
                                  tmp    usr
@@ -748,17 +785,20 @@ replicas continue to be exactly same.
                    How do we set up the same tree at multiple locations under
                    /root/tmp
 
-               step2:
-                     mount --bind /root/tmp /root/tmp
+               step 2:
+                     ::
 
-                     mount --make-rshared /root
-                     mount --make-unbindable /root/tmp
 
-                     mkdir -p /tmp/m1
+                       mount --bind /root/tmp /root/tmp
 
-                     mount --rbind /root /tmp/m1
+                       mount --make-rshared /root
+                       mount --make-unbindable /root/tmp
 
-                     the new tree now looks like this:
+                       mkdir -p /tmp/m1
+
+                       mount --rbind /root /tmp/m1
+
+                     the new tree now looks like this::
 
                                    root
                                   /    \
@@ -768,11 +808,13 @@ replicas continue to be exactly same.
                              /  \
                             tmp  usr
 
-               step3:
+               step 3:
+                     ::
+
                            mkdir -p /tmp/m2
                            mount --rbind /root /tmp/m2
 
-                     the new tree now looks like this:
+                     the new tree now looks like this::
 
                                    root
                                   /    \
@@ -782,12 +824,13 @@ replicas continue to be exactly same.
                              /  \     / \
                             tmp  usr tmp usr
 
-               step4:
+               step 4:
+                     ::
 
                            mkdir -p /tmp/m3
                            mount --rbind /root /tmp/m3
 
-                     the new tree now looks like this:
+                     the new tree now looks like this::
 
                                          root
                                      /           \
@@ -801,25 +844,31 @@ replicas continue to be exactly same.
 
 8A) Datastructure
 
-       4 new fields are introduced to struct vfsmount
-       ->mnt_share
-       ->mnt_slave_list
-       ->mnt_slave
-       ->mnt_master
+       4 new fields are introduced to struct vfsmount:
+
+       *   ->mnt_share
+       *   ->mnt_slave_list
+       *   ->mnt_slave
+       *   ->mnt_master
 
-       ->mnt_share links together all the mount to/from which this vfsmount
+       ->mnt_share
+               links together all the mount to/from which this vfsmount
                send/receives propagation events.
 
-       ->mnt_slave_list links all the mounts to which this vfsmount propagates
+       ->mnt_slave_list
+               links all the mounts to which this vfsmount propagates
                to.
 
-       ->mnt_slave links together all the slaves that its master vfsmount
+       ->mnt_slave
+               links together all the slaves that its master vfsmount
                propagates to.
 
-       ->mnt_master points to the master vfsmount from which this vfsmount
+       ->mnt_master
+               points to the master vfsmount from which this vfsmount
                receives propagation.
 
-       ->mnt_flags takes two more flags to indicate the propagation status of
+       ->mnt_flags
+               takes two more flags to indicate the propagation status of
                the vfsmount.  MNT_SHARE indicates that the vfsmount is a shared
                vfsmount.  MNT_UNCLONABLE indicates that the vfsmount cannot be
                replicated.
@@ -842,7 +891,7 @@ replicas continue to be exactly same.
 
        A example propagation tree looks as shown in the figure below.
        [ NOTE: Though it looks like a forest, if we consider all the shared
-       mounts as a conceptual entity called 'pnode', it becomes a tree]
+       mounts as a conceptual entity called 'pnode', it becomes a tree]::
 
 
                        A <--> B <--> C <---> D
@@ -864,14 +913,19 @@ replicas continue to be exactly same.
        A's ->mnt_slave_list links with ->mnt_slave of 'E', 'K', 'F' and 'G'
 
        E's ->mnt_share links with ->mnt_share of K
-       'E', 'K', 'F', 'G' have their ->mnt_master point to struct
-                               vfsmount of 'A'
+
+       'E', 'K', 'F', 'G' have their ->mnt_master point to struct vfsmount of 'A'
+
        'M', 'L', 'N' have their ->mnt_master point to struct vfsmount of 'K'
+
        K's ->mnt_slave_list links with ->mnt_slave of 'M', 'L' and 'N'
 
        C's ->mnt_slave_list links with ->mnt_slave of 'J' and 'K'
+
        J and K's ->mnt_master points to struct vfsmount of C
+
        and finally D's ->mnt_slave_list links with ->mnt_slave of 'H' and 'I'
+
        'H' and 'I' have their ->mnt_master pointing to struct vfsmount of 'D'.
 
 
@@ -903,6 +957,7 @@ replicas continue to be exactly same.
        Prepare phase:
 
        for each mount in the source tree:
+
                   a) Create the necessary number of mount trees to
                        be attached to each of the mounts that receive
                        propagation from the destination mount.
@@ -929,11 +984,12 @@ replicas continue to be exactly same.
        Abort phase
                delete all the newly created trees.
 
-       NOTE: all the propagation related functionality resides in the file
-       pnode.c
+       .. Note::
+          all the propagation related functionality resides in the file pnode.c
 
 
 ------------------------------------------------------------------------
 
 version 0.1  (created the initial document, Ram Pai linuxram@us.ibm.com)
+
 version 0.2  (Incorporated comments from Al Viro)
diff --git a/Documentation/filesystems/spufs/index.rst b/Documentation/filesystems/spufs/index.rst
new file mode 100644 (file)
index 0000000..5ed4a84
--- /dev/null
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============
+SPU Filesystem
+==============
+
+
+.. toctree::
+   :maxdepth: 1
+
+   spufs
+   spu_create
+   spu_run
diff --git a/Documentation/filesystems/spufs/spu_create.rst b/Documentation/filesystems/spufs/spu_create.rst
new file mode 100644 (file)
index 0000000..83108c0
--- /dev/null
@@ -0,0 +1,131 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========
+spu_create
+==========
+
+Name
+====
+       spu_create - create a new spu context
+
+
+Synopsis
+========
+
+       ::
+
+         #include <sys/types.h>
+         #include <sys/spu.h>
+
+         int spu_create(const char *pathname, int flags, mode_t mode);
+
+Description
+===========
+       The  spu_create  system call is used on PowerPC machines that implement
+       the Cell Broadband Engine Architecture in order to  access  Synergistic
+       Processor  Units (SPUs). It creates a new logical context for an SPU in
+       pathname and returns a handle to associated  with  it.   pathname  must
+       point  to  a  non-existing directory in the mount point of the SPU file
+       system (spufs).  When spu_create is successful, a directory  gets  cre-
+       ated on pathname and it is populated with files.
+
+       The  returned  file  handle can only be passed to spu_run(2) or closed,
+       other operations are not defined on it. When it is closed, all  associ-
+       ated  directory entries in spufs are removed. When the last file handle
+       pointing either inside  of  the  context  directory  or  to  this  file
+       descriptor is closed, the logical SPU context is destroyed.
+
+       The  parameter flags can be zero or any bitwise or'd combination of the
+       following constants:
+
+       SPU_RAWIO
+              Allow mapping of some of the hardware registers of the SPU  into
+              user space. This flag requires the CAP_SYS_RAWIO capability, see
+              capabilities(7).
+
+       The mode parameter specifies the permissions used for creating the  new
+       directory  in  spufs.   mode is modified with the user's umask(2) value
+       and then used for both the directory and the files contained in it. The
+       file permissions mask out some more bits of mode because they typically
+       support only read or write access. See stat(2) for a full list  of  the
+       possible mode values.
+
+
+Return Value
+============
+       spu_create  returns a new file descriptor. It may return -1 to indicate
+       an error condition and set errno to  one  of  the  error  codes  listed
+       below.
+
+
+Errors
+======
+       EACCES
+              The  current  user does not have write access on the spufs mount
+              point.
+
+       EEXIST An SPU context already exists at the given path name.
+
+       EFAULT pathname is not a valid string pointer in  the  current  address
+              space.
+
+       EINVAL pathname is not a directory in the spufs mount point.
+
+       ELOOP  Too many symlinks were found while resolving pathname.
+
+       EMFILE The process has reached its maximum open file limit.
+
+       ENAMETOOLONG
+              pathname was too long.
+
+       ENFILE The system has reached the global open file limit.
+
+       ENOENT Part of pathname could not be resolved.
+
+       ENOMEM The kernel could not allocate all resources required.
+
+       ENOSPC There  are  not  enough  SPU resources available to create a new
+              context or the user specific limit for the number  of  SPU  con-
+              texts has been reached.
+
+       ENOSYS the functionality is not provided by the current system, because
+              either the hardware does not provide SPUs or the spufs module is
+              not loaded.
+
+       ENOTDIR
+              A part of pathname is not a directory.
+
+
+
+Notes
+=====
+       spu_create  is  meant  to  be used from libraries that implement a more
+       abstract interface to SPUs, not to be used from  regular  applications.
+       See  http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
+       ommended libraries.
+
+
+Files
+=====
+       pathname must point to a location beneath the mount point of spufs.  By
+       convention, it gets mounted in /spu.
+
+
+Conforming to
+=============
+       This call is Linux specific and only implemented by the ppc64 architec-
+       ture. Programs using this system call are not portable.
+
+
+Bugs
+====
+       The code does not yet fully implement all features lined out here.
+
+
+Author
+======
+       Arnd Bergmann <arndb@de.ibm.com>
+
+See Also
+========
+       capabilities(7), close(2), spu_run(2), spufs(7)
diff --git a/Documentation/filesystems/spufs/spu_run.rst b/Documentation/filesystems/spufs/spu_run.rst
new file mode 100644 (file)
index 0000000..7fdb1c3
--- /dev/null
@@ -0,0 +1,138 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======
+spu_run
+=======
+
+
+Name
+====
+       spu_run - execute an spu context
+
+
+Synopsis
+========
+
+       ::
+
+           #include <sys/spu.h>
+
+           int spu_run(int fd, unsigned int *npc, unsigned int *event);
+
+Description
+===========
+       The  spu_run system call is used on PowerPC machines that implement the
+       Cell Broadband Engine Architecture in order to access Synergistic  Pro-
+       cessor  Units  (SPUs).  It  uses the fd that was returned from spu_cre-
+       ate(2) to address a specific SPU context. When the context gets  sched-
+       uled  to a physical SPU, it starts execution at the instruction pointer
+       passed in npc.
+
+       Execution of SPU code happens synchronously, meaning that spu_run  does
+       not  return  while the SPU is still running. If there is a need to exe-
+       cute SPU code in parallel with other code on either  the  main  CPU  or
+       other  SPUs,  you  need to create a new thread of execution first, e.g.
+       using the pthread_create(3) call.
+
+       When spu_run returns, the current value of the SPU instruction  pointer
+       is  written back to npc, so you can call spu_run again without updating
+       the pointers.
+
+       event can be a NULL pointer or point to an extended  status  code  that
+       gets  filled  when spu_run returns. It can be one of the following con-
+       stants:
+
+       SPE_EVENT_DMA_ALIGNMENT
+              A DMA alignment error
+
+       SPE_EVENT_SPE_DATA_SEGMENT
+              A DMA segmentation error
+
+       SPE_EVENT_SPE_DATA_STORAGE
+              A DMA storage error
+
+       If NULL is passed as the event argument, these errors will result in  a
+       signal delivered to the calling process.
+
+Return Value
+============
+       spu_run  returns the value of the spu_status register or -1 to indicate
+       an error and set errno to one of the error  codes  listed  below.   The
+       spu_status  register  value  contains  a  bit  mask of status codes and
+       optionally a 14 bit code returned from the stop-and-signal  instruction
+       on the SPU. The bit masks for the status codes are:
+
+       0x02
+             SPU was stopped by stop-and-signal.
+
+       0x04
+             SPU was stopped by halt.
+
+       0x08
+             SPU is waiting for a channel.
+
+       0x10
+             SPU is in single-step mode.
+
+       0x20
+             SPU has tried to execute an invalid instruction.
+
+       0x40
+             SPU has tried to access an invalid channel.
+
+       0x3fff0000
+              The  bits  masked with this value contain the code returned from
+              stop-and-signal.
+
+       There are always one or more of the lower eight bits set  or  an  error
+       code is returned from spu_run.
+
+Errors
+======
+       EAGAIN or EWOULDBLOCK
+              fd is in non-blocking mode and spu_run would block.
+
+       EBADF  fd is not a valid file descriptor.
+
+       EFAULT npc is not a valid pointer or status is neither NULL nor a valid
+              pointer.
+
+       EINTR  A signal occurred while spu_run was in progress.  The npc  value
+              has  been updated to the new program counter value if necessary.
+
+       EINVAL fd is not a file descriptor returned from spu_create(2).
+
+       ENOMEM Insufficient memory was available to handle a page fault result-
+              ing from an MFC direct memory access.
+
+       ENOSYS the functionality is not provided by the current system, because
+              either the hardware does not provide SPUs or the spufs module is
+              not loaded.
+
+
+Notes
+=====
+       spu_run  is  meant  to  be  used  from  libraries that implement a more
+       abstract interface to SPUs, not to be used from  regular  applications.
+       See  http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
+       ommended libraries.
+
+
+Conforming to
+=============
+       This call is Linux specific and only implemented by the ppc64 architec-
+       ture. Programs using this system call are not portable.
+
+
+Bugs
+====
+       The code does not yet fully implement all features lined out here.
+
+
+Author
+======
+       Arnd Bergmann <arndb@de.ibm.com>
+
+See Also
+========
+       capabilities(7), close(2), spu_create(2), spufs(7)
similarity index 57%
rename from Documentation/filesystems/spufs.txt
rename to Documentation/filesystems/spufs/spufs.rst
index eb9e3aa63026503a070ff77271bee35884348078..8a42859bb10083fc471f394b8c150f114a35e6b2 100644 (file)
@@ -1,12 +1,18 @@
-SPUFS(2)                   Linux Programmer's Manual                  SPUFS(2)
+.. SPDX-License-Identifier: GPL-2.0
 
+=====
+spufs
+=====
 
+Name
+====
 
-NAME
        spufs - the SPU file system
 
 
-DESCRIPTION
+Description
+===========
+
        The SPU file system is used on PowerPC machines that implement the Cell
        Broadband Engine Architecture in order to access Synergistic  Processor
        Units (SPUs).
@@ -21,7 +27,9 @@ DESCRIPTION
        ally add or remove files.
 
 
-MOUNT OPTIONS
+Mount Options
+=============
+
        uid=<uid>
               set the user owning the mount point, the default is 0 (root).
 
@@ -29,7 +37,9 @@ MOUNT OPTIONS
               set the group owning the mount point, the default is 0 (root).
 
 
-FILES
+Files
+=====
+
        The files in spufs mostly follow the standard behavior for regular sys-
        tem  calls like read(2) or write(2), but often support only a subset of
        the operations supported on regular file systems. This list details the
@@ -125,14 +135,12 @@ FILES
               space is available for writing.
 
 
-   /mbox_stat
-   /ibox_stat
-   /wbox_stat
+   /mbox_stat, /ibox_stat, /wbox_stat
        Read-only files that contain the length of the current queue, i.e.  how
        many  words  can  be  read  from  mbox or ibox or how many words can be
        written to wbox without blocking.  The files can be read only in 4-byte
        units  and  return  a  big-endian  binary integer number.  The possible
-       operations on an open *box_stat file are:
+       operations on an open ``*box_stat`` file are:
 
        read(2)
               If a count smaller than four is requested, read returns  -1  and
@@ -143,12 +151,7 @@ FILES
               in EAGAIN.
 
 
-   /npc
-   /decr
-   /decr_status
-   /spu_tag_mask
-   /event_mask
-   /srr0
+   /npc, /decr, /decr_status, /spu_tag_mask, /event_mask, /srr0
        Internal  registers  of  the SPU. The representation is an ASCII string
        with the numeric value of the next instruction to  be  executed.  These
        can  be  used in read/write mode for debugging, but normal operation of
@@ -157,17 +160,14 @@ FILES
 
        The contents of these files are:
 
+       =================== ===================================
        npc                 Next Program Counter
-
        decr                SPU Decrementer
-
        decr_status         Decrementer Status
-
        spu_tag_mask        MFC tag mask for SPU DMA
-
        event_mask          Event mask for SPU interrupts
-
        srr0                Interrupt Return address register
+       =================== ===================================
 
 
        The   possible   operations   on   an   open  npc,  decr,  decr_status,
@@ -206,8 +206,7 @@ FILES
               from the data buffer, updating the value of the fpcr register.
 
 
-   /signal1
-   /signal2
+   /signal1, /signal2
        The two signal notification channels of an SPU.  These  are  read-write
        files  that  operate  on  a 32 bit word.  Writing to one of these files
        triggers an interrupt on the SPU.  The  value  written  to  the  signal
@@ -233,8 +232,7 @@ FILES
               file.
 
 
-   /signal1_type
-   /signal2_type
+   /signal1_type, /signal2_type
        These two files change the behavior of the signal1 and signal2  notifi-
        cation  files.  The  contain  a numerical ASCII string which is read as
        either "1" or "0".  In mode 0 (overwrite), the  hardware  replaces  the
@@ -259,263 +257,17 @@ FILES
               the previous setting.
 
 
-EXAMPLES
+Examples
+========
        /etc/fstab entry
               none      /spu      spufs     gid=spu   0    0
 
 
-AUTHORS
+Authors
+=======
        Arnd  Bergmann  <arndb@de.ibm.com>,  Mark  Nutter <mnutter@us.ibm.com>,
        Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
 
-SEE ALSO
+See Also
+========
        capabilities(7), close(2), spu_create(2), spu_run(2), spufs(7)
-
-
-
-Linux                             2005-09-28                          SPUFS(2)
-
-------------------------------------------------------------------------------
-
-SPU_RUN(2)                 Linux Programmer's Manual                SPU_RUN(2)
-
-
-
-NAME
-       spu_run - execute an spu context
-
-
-SYNOPSIS
-       #include <sys/spu.h>
-
-       int spu_run(int fd, unsigned int *npc, unsigned int *event);
-
-DESCRIPTION
-       The  spu_run system call is used on PowerPC machines that implement the
-       Cell Broadband Engine Architecture in order to access Synergistic  Pro-
-       cessor  Units  (SPUs).  It  uses the fd that was returned from spu_cre-
-       ate(2) to address a specific SPU context. When the context gets  sched-
-       uled  to a physical SPU, it starts execution at the instruction pointer
-       passed in npc.
-
-       Execution of SPU code happens synchronously, meaning that spu_run  does
-       not  return  while the SPU is still running. If there is a need to exe-
-       cute SPU code in parallel with other code on either  the  main  CPU  or
-       other  SPUs,  you  need to create a new thread of execution first, e.g.
-       using the pthread_create(3) call.
-
-       When spu_run returns, the current value of the SPU instruction  pointer
-       is  written back to npc, so you can call spu_run again without updating
-       the pointers.
-
-       event can be a NULL pointer or point to an extended  status  code  that
-       gets  filled  when spu_run returns. It can be one of the following con-
-       stants:
-
-       SPE_EVENT_DMA_ALIGNMENT
-              A DMA alignment error
-
-       SPE_EVENT_SPE_DATA_SEGMENT
-              A DMA segmentation error
-
-       SPE_EVENT_SPE_DATA_STORAGE
-              A DMA storage error
-
-       If NULL is passed as the event argument, these errors will result in  a
-       signal delivered to the calling process.
-
-RETURN VALUE
-       spu_run  returns the value of the spu_status register or -1 to indicate
-       an error and set errno to one of the error  codes  listed  below.   The
-       spu_status  register  value  contains  a  bit  mask of status codes and
-       optionally a 14 bit code returned from the stop-and-signal  instruction
-       on the SPU. The bit masks for the status codes are:
-
-       0x02   SPU was stopped by stop-and-signal.
-
-       0x04   SPU was stopped by halt.
-
-       0x08   SPU is waiting for a channel.
-
-       0x10   SPU is in single-step mode.
-
-       0x20   SPU has tried to execute an invalid instruction.
-
-       0x40   SPU has tried to access an invalid channel.
-
-       0x3fff0000
-              The  bits  masked with this value contain the code returned from
-              stop-and-signal.
-
-       There are always one or more of the lower eight bits set  or  an  error
-       code is returned from spu_run.
-
-ERRORS
-       EAGAIN or EWOULDBLOCK
-              fd is in non-blocking mode and spu_run would block.
-
-       EBADF  fd is not a valid file descriptor.
-
-       EFAULT npc is not a valid pointer or status is neither NULL nor a valid
-              pointer.
-
-       EINTR  A signal occurred while spu_run was in progress.  The npc  value
-              has  been updated to the new program counter value if necessary.
-
-       EINVAL fd is not a file descriptor returned from spu_create(2).
-
-       ENOMEM Insufficient memory was available to handle a page fault result-
-              ing from an MFC direct memory access.
-
-       ENOSYS the functionality is not provided by the current system, because
-              either the hardware does not provide SPUs or the spufs module is
-              not loaded.
-
-
-NOTES
-       spu_run  is  meant  to  be  used  from  libraries that implement a more
-       abstract interface to SPUs, not to be used from  regular  applications.
-       See  http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
-       ommended libraries.
-
-
-CONFORMING TO
-       This call is Linux specific and only implemented by the ppc64 architec-
-       ture. Programs using this system call are not portable.
-
-
-BUGS
-       The code does not yet fully implement all features lined out here.
-
-
-AUTHOR
-       Arnd Bergmann <arndb@de.ibm.com>
-
-SEE ALSO
-       capabilities(7), close(2), spu_create(2), spufs(7)
-
-
-
-Linux                             2005-09-28                        SPU_RUN(2)
-
-------------------------------------------------------------------------------
-
-SPU_CREATE(2)              Linux Programmer's Manual             SPU_CREATE(2)
-
-
-
-NAME
-       spu_create - create a new spu context
-
-
-SYNOPSIS
-       #include <sys/types.h>
-       #include <sys/spu.h>
-
-       int spu_create(const char *pathname, int flags, mode_t mode);
-
-DESCRIPTION
-       The  spu_create  system call is used on PowerPC machines that implement
-       the Cell Broadband Engine Architecture in order to  access  Synergistic
-       Processor  Units (SPUs). It creates a new logical context for an SPU in
-       pathname and returns a handle to associated  with  it.   pathname  must
-       point  to  a  non-existing directory in the mount point of the SPU file
-       system (spufs).  When spu_create is successful, a directory  gets  cre-
-       ated on pathname and it is populated with files.
-
-       The  returned  file  handle can only be passed to spu_run(2) or closed,
-       other operations are not defined on it. When it is closed, all  associ-
-       ated  directory entries in spufs are removed. When the last file handle
-       pointing either inside  of  the  context  directory  or  to  this  file
-       descriptor is closed, the logical SPU context is destroyed.
-
-       The  parameter flags can be zero or any bitwise or'd combination of the
-       following constants:
-
-       SPU_RAWIO
-              Allow mapping of some of the hardware registers of the SPU  into
-              user space. This flag requires the CAP_SYS_RAWIO capability, see
-              capabilities(7).
-
-       The mode parameter specifies the permissions used for creating the  new
-       directory  in  spufs.   mode is modified with the user's umask(2) value
-       and then used for both the directory and the files contained in it. The
-       file permissions mask out some more bits of mode because they typically
-       support only read or write access. See stat(2) for a full list  of  the
-       possible mode values.
-
-
-RETURN VALUE
-       spu_create  returns a new file descriptor. It may return -1 to indicate
-       an error condition and set errno to  one  of  the  error  codes  listed
-       below.
-
-
-ERRORS
-       EACCES
-              The  current  user does not have write access on the spufs mount
-              point.
-
-       EEXIST An SPU context already exists at the given path name.
-
-       EFAULT pathname is not a valid string pointer in  the  current  address
-              space.
-
-       EINVAL pathname is not a directory in the spufs mount point.
-
-       ELOOP  Too many symlinks were found while resolving pathname.
-
-       EMFILE The process has reached its maximum open file limit.
-
-       ENAMETOOLONG
-              pathname was too long.
-
-       ENFILE The system has reached the global open file limit.
-
-       ENOENT Part of pathname could not be resolved.
-
-       ENOMEM The kernel could not allocate all resources required.
-
-       ENOSPC There  are  not  enough  SPU resources available to create a new
-              context or the user specific limit for the number  of  SPU  con-
-              texts has been reached.
-
-       ENOSYS the functionality is not provided by the current system, because
-              either the hardware does not provide SPUs or the spufs module is
-              not loaded.
-
-       ENOTDIR
-              A part of pathname is not a directory.
-
-
-
-NOTES
-       spu_create  is  meant  to  be used from libraries that implement a more
-       abstract interface to SPUs, not to be used from  regular  applications.
-       See  http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
-       ommended libraries.
-
-
-FILES
-       pathname must point to a location beneath the mount point of spufs.  By
-       convention, it gets mounted in /spu.
-
-
-CONFORMING TO
-       This call is Linux specific and only implemented by the ppc64 architec-
-       ture. Programs using this system call are not portable.
-
-
-BUGS
-       The code does not yet fully implement all features lined out here.
-
-
-AUTHOR
-       Arnd Bergmann <arndb@de.ibm.com>
-
-SEE ALSO
-       capabilities(7), close(2), spu_run(2), spufs(7)
-
-
-
-Linux                             2005-09-28                     SPU_CREATE(2)
similarity index 92%
rename from Documentation/filesystems/sysfs-pci.txt
rename to Documentation/filesystems/sysfs-pci.rst
index 06f1d64c6f702fa2507b748b6feb5a6bddf20975..a265f3e2cc80ac5e7b12541e573838098e4da31f 100644 (file)
@@ -1,8 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================
 Accessing PCI device resources through sysfs
---------------------------------------------
+============================================
 
 sysfs, usually mounted at /sys, provides access to PCI resources on platforms
-that support it.  For example, a given bus might look like this:
+that support it.  For example, a given bus might look like this::
 
      /sys/devices/pci0000:17
      |-- 0000:17:00.0
@@ -30,8 +33,9 @@ This bus contains a single function device in slot 0.  The domain and bus
 numbers are reproduced for convenience.  Under the device directory are several
 files, each with their own function.
 
+       =================== =====================================================
        file               function
-       ----               --------
+       =================== =====================================================
        class              PCI class (ascii, ro)
        config             PCI config space (binary, rw)
        device             PCI device (ascii, ro)
@@ -40,13 +44,16 @@ files, each with their own function.
        local_cpus         nearby CPU mask (cpumask, ro)
        remove             remove device from kernel's list (ascii, wo)
        resource                   PCI resource host addresses (ascii, ro)
-       resource0..N       PCI resource N, if present (binary, mmap, rw[1])
+       resource0..N       PCI resource N, if present (binary, mmap, rw\ [1]_)
        resource0_wc..N_wc  PCI WC map resource N, if prefetchable (binary, mmap)
        revision                   PCI revision (ascii, ro)
        rom                PCI ROM resource, if present (binary, ro)
        subsystem_device           PCI subsystem device (ascii, ro)
        subsystem_vendor           PCI subsystem vendor (ascii, ro)
        vendor             PCI vendor (ascii, ro)
+       =================== =====================================================
+
+::
 
   ro - read only file
   rw - file is readable and writable
@@ -56,7 +63,7 @@ files, each with their own function.
   binary - file contains binary data
   cpumask - file contains a cpumask type
 
-[1] rw for RESOURCE_IO (I/O port) regions only
+.. [1] rw for RESOURCE_IO (I/O port) regions only
 
 The read only files are informational, writes to them will be ignored, with
 the exception of the 'rom' file.  Writable files can be used to perform
@@ -67,11 +74,11 @@ don't support mmapping of certain resources, so be sure to check the return
 value from any attempted mmap.  The most notable of these are I/O port
 resources, which also provide read/write access.
 
-The 'enable' file provides a counter that indicates how many times the device 
+The 'enable' file provides a counter that indicates how many times the device
 has been enabled.  If the 'enable' file currently returns '4', and a '1' is
 echoed into it, it will then return '5'.  Echoing a '0' into it will decrease
 the count.  Even when it returns to 0, though, some of the initialisation
-may not be reversed.  
+may not be reversed.
 
 The 'rom' file is special in that it provides read-only access to the device's
 ROM file, if available.  It's disabled by default, however, so applications
@@ -93,7 +100,7 @@ Accessing legacy resources through sysfs
 
 Legacy I/O port and ISA memory resources are also provided in sysfs if the
 underlying platform supports them.  They're located in the PCI class hierarchy,
-e.g.
+e.g.::
 
        /sys/class/pci_bus/0000:17/
        |-- bridge -> ../../../devices/pci0000:17
similarity index 72%
rename from Documentation/filesystems/sysfs-tagging.txt
rename to Documentation/filesystems/sysfs-tagging.rst
index c7c8e643895844412f62ac3469229b564d7ee5e5..8888a05c398ef67ff22dfb3cd9683c234fc359e6 100644 (file)
@@ -1,5 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
 Sysfs tagging
--------------
+=============
 
 (Taken almost verbatim from Eric Biederman's netns tagging patch
 commit msg)
@@ -18,25 +21,28 @@ in the directories and applications only see a limited set of
 the network devices.
 
 Each sysfs directory entry may be tagged with a namespace via the
-void *ns member of its kernfs_node.  If a directory entry is tagged,
-then kernfs_node->flags will have a flag between KOBJ_NS_TYPE_NONE
+``void *ns member`` of its ``kernfs_node``.  If a directory entry is tagged,
+then ``kernfs_node->flags`` will have a flag between KOBJ_NS_TYPE_NONE
 and KOBJ_NS_TYPES, and ns will point to the namespace to which it
 belongs.
 
-Each sysfs superblock's kernfs_super_info contains an array void
-*ns[KOBJ_NS_TYPES].  When a task in a tagging namespace
+Each sysfs superblock's kernfs_super_info contains an array
+``void *ns[KOBJ_NS_TYPES]``.  When a task in a tagging namespace
 kobj_nstype first mounts sysfs, a new superblock is created.  It
 will be differentiated from other sysfs mounts by having its
-s_fs_info->ns[kobj_nstype] set to the new namespace.  Note that
+``s_fs_info->ns[kobj_nstype]`` set to the new namespace.  Note that
 through bind mounting and mounts propagation, a task can easily view
 the contents of other namespaces' sysfs mounts.  Therefore, when a
 namespace exits, it will call kobj_ns_exit() to invalidate any
 kernfs_node->ns pointers pointing to it.
 
 Users of this interface:
-- define a type in the kobj_ns_type enumeration.
-- call kobj_ns_type_register() with its kobj_ns_type_operations which has
+
+- define a type in the ``kobj_ns_type`` enumeration.
+- call kobj_ns_type_register() with its ``kobj_ns_type_operations`` which has
+
   - current_ns() which returns current's namespace
   - netlink_ns() which returns a socket's namespace
   - initial_ns() which returns the initial namesapce
+
 - call kobj_ns_exit() when an individual tag is no longer valid
index 290891c3fecbb77b102ac8a1e25d304d2dd56b66..ab0f7795792b5bcd647f60905cbf1020e246e3a7 100644 (file)
@@ -20,7 +20,7 @@ a means to export kernel data structures, their attributes, and the
 linkages between them to userspace.
 
 sysfs is tied inherently to the kobject infrastructure. Please read
-Documentation/kobject.txt for more information concerning the kobject
+Documentation/core-api/kobject.rst for more information concerning the kobject
 interface.
 
 
index 7d4d09dd5e6dec98bfb062da7e11d9996dd106d5..ed17771c212b8e49520acbb587cd24fd806c274b 100644 (file)
@@ -706,6 +706,7 @@ cache in your filesystem.  The following members are defined:
                int (*readpage)(struct file *, struct page *);
                int (*writepages)(struct address_space *, struct writeback_control *);
                int (*set_page_dirty)(struct page *page);
+               void (*readahead)(struct readahead_control *);
                int (*readpages)(struct file *filp, struct address_space *mapping,
                                 struct list_head *pages, unsigned nr_pages);
                int (*write_begin)(struct file *, struct address_space *mapping,
@@ -781,12 +782,26 @@ cache in your filesystem.  The following members are defined:
        If defined, it should set the PageDirty flag, and the
        PAGECACHE_TAG_DIRTY tag in the radix tree.
 
+``readahead``
+       Called by the VM to read pages associated with the address_space
+       object.  The pages are consecutive in the page cache and are
+       locked.  The implementation should decrement the page refcount
+       after starting I/O on each page.  Usually the page will be
+       unlocked by the I/O completion handler.  If the filesystem decides
+       to stop attempting I/O before reaching the end of the readahead
+       window, it can simply return.  The caller will decrement the page
+       refcount and unlock the remaining pages for you.  Set PageUptodate
+       if the I/O completes successfully.  Setting PageError on any page
+       will be ignored; simply unlock the page if an I/O error occurs.
+
 ``readpages``
        called by the VM to read pages associated with the address_space
        object.  This is essentially just a vector version of readpage.
        Instead of just one page, several pages are requested.
        readpages is only used for read-ahead, so read errors are
        ignored.  If anything goes wrong, feel free to give up.
+       This interface is deprecated and will be removed by the end of
+       2020; implement readahead instead.
 
 ``write_begin``
        Called by the generic buffered write code to ask the filesystem
similarity index 97%
rename from Documentation/filesystems/xfs-delayed-logging-design.txt
rename to Documentation/filesystems/xfs-delayed-logging-design.rst
index 9a6dd289b17bbcd67835e6f33ad7c667b3473199..464405d2801e532dad4102ac8ff59ce6e722315c 100644 (file)
@@ -1,8 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================
 XFS Delayed Logging Design
---------------------------
+==========================
 
 Introduction to Re-logging in XFS
----------------------------------
+=================================
 
 XFS logging is a combination of logical and physical logging. Some objects,
 such as inodes and dquots, are logged in logical format where the details
@@ -25,7 +28,7 @@ changes in the new transaction that is written to the log.
 That is, if we have a sequence of changes A through to F, and the object was
 written to disk after change D, we would see in the log the following series
 of transactions, their contents and the log sequence number (LSN) of the
-transaction:
+transaction::
 
        Transaction             Contents        LSN
           A                       A               X
@@ -85,7 +88,7 @@ IO permanently. Hence the XFS journalling subsystem can be considered to be IO
 bound.
 
 Delayed Logging: Concepts
--------------------------
+=========================
 
 The key thing to note about the asynchronous logging combined with the
 relogging technique XFS uses is that we can be relogging changed objects
@@ -154,9 +157,10 @@ The fundamental requirements for delayed logging in XFS are simple:
        6. No performance regressions for synchronous transaction workloads.
 
 Delayed Logging: Design
------------------------
+=======================
 
 Storing Changes
+---------------
 
 The problem with accumulating changes at a logical level (i.e. just using the
 existing log item dirty region tracking) is that when it comes to writing the
@@ -194,30 +198,30 @@ asynchronous transactions to the log. The differences between the existing
 formatting method and the delayed logging formatting can be seen in the
 diagram below.
 
-Current format log vector:
+Current format log vector::
 
-Object    +---------------------------------------------+
-Vector 1      +----+
-Vector 2                    +----+
-Vector 3                                   +----------+
+    Object    +---------------------------------------------+
+    Vector 1      +----+
+    Vector 2                    +----+
+    Vector 3                                   +----------+
 
-After formatting:
+After formatting::
 
-Log Buffer    +-V1-+-V2-+----V3----+
+    Log Buffer    +-V1-+-V2-+----V3----+
 
-Delayed logging vector:
+Delayed logging vector::
 
-Object    +---------------------------------------------+
-Vector 1      +----+
-Vector 2                    +----+
-Vector 3                                   +----------+
+    Object    +---------------------------------------------+
+    Vector 1      +----+
+    Vector 2                    +----+
+    Vector 3                                   +----------+
 
-After formatting:
+After formatting::
 
-Memory Buffer +-V1-+-V2-+----V3----+
-Vector 1      +----+
-Vector 2           +----+
-Vector 3                +----------+
+    Memory Buffer +-V1-+-V2-+----V3----+
+    Vector 1      +----+
+    Vector 2           +----+
+    Vector 3                +----------+
 
 The memory buffer and associated vector need to be passed as a single object,
 but still need to be associated with the parent object so if the object is
@@ -242,6 +246,7 @@ relogged in memory.
 
 
 Tracking Changes
+----------------
 
 Now that we can record transactional changes in memory in a form that allows
 them to be used without limitations, we need to be able to track and accumulate
@@ -278,6 +283,7 @@ done for convenience/sanity of the developers.
 
 
 Delayed Logging: Checkpoints
+----------------------------
 
 When we have a log synchronisation event, commonly known as a "log force",
 all the items in the CIL must be written into the log via the log buffers.
@@ -341,7 +347,7 @@ Hence log vectors need to be able to be chained together to allow them to be
 detached from the log items. That is, when the CIL is flushed the memory
 buffer and log vector attached to each log item needs to be attached to the
 checkpoint context so that the log item can be released. In diagrammatic form,
-the CIL would look like this before the flush:
+the CIL would look like this before the flush::
 
        CIL Head
           |
@@ -362,7 +368,7 @@ the CIL would look like this before the flush:
                                        -> vector array
 
 And after the flush the CIL head is empty, and the checkpoint context log
-vector list would look like:
+vector list would look like::
 
        Checkpoint Context
           |
@@ -411,6 +417,7 @@ compare" situation that can be done after a working and reviewed implementation
 is in the dev tree....
 
 Delayed Logging: Checkpoint Sequencing
+--------------------------------------
 
 One of the key aspects of the XFS transaction subsystem is that it tags
 committed transactions with the log sequence number of the transaction commit.
@@ -474,6 +481,7 @@ force the log at the LSN of that transaction) and so the higher level code
 behaves the same regardless of whether delayed logging is being used or not.
 
 Delayed Logging: Checkpoint Log Space Accounting
+------------------------------------------------
 
 The big issue for a checkpoint transaction is the log space reservation for the
 transaction. We don't know how big a checkpoint transaction is going to be
@@ -491,7 +499,7 @@ the size of the transaction and the number of regions being logged (the number
 of log vectors in the transaction).
 
 An example of the differences would be logging directory changes versus logging
-inode changes. If you modify lots of inode cores (e.g. chmod -R g+w *), then
+inode changes. If you modify lots of inode cores (e.g. ``chmod -R g+w *``), then
 there are lots of transactions that only contain an inode core and an inode log
 format structure. That is, two vectors totaling roughly 150 bytes. If we modify
 10,000 inodes, we have about 1.5MB of metadata to write in 20,000 vectors. Each
@@ -565,6 +573,7 @@ which is once every 30s.
 
 
 Delayed Logging: Log Item Pinning
+---------------------------------
 
 Currently log items are pinned during transaction commit while the items are
 still locked. This happens just after the items are formatted, though it could
@@ -605,6 +614,7 @@ object, we have a race with CIL being flushed between the check and the pin
 lock to guarantee that we pin the items correctly.
 
 Delayed Logging: Concurrent Scalability
+---------------------------------------
 
 A fundamental requirement for the CIL is that accesses through transaction
 commits must scale to many concurrent commits. The current transaction commit
@@ -683,8 +693,9 @@ woken by the wrong event.
 
 
 Lifecycle Changes
+-----------------
 
-The existing log item life cycle is as follows:
+The existing log item life cycle is as follows::
 
        1. Transaction allocate
        2. Transaction reserve
@@ -729,7 +740,7 @@ at the same time. If the log item is in the AIL or between steps 6 and 7
 and steps 1-6 are re-entered, then the item is relogged. Only when steps 8-9
 are entered and completed is the object considered clean.
 
-With delayed logging, there are new steps inserted into the life cycle:
+With delayed logging, there are new steps inserted into the life cycle::
 
        1. Transaction allocate
        2. Transaction reserve
similarity index 83%
rename from Documentation/filesystems/xfs-self-describing-metadata.txt
rename to Documentation/filesystems/xfs-self-describing-metadata.rst
index 8db0121d0980c4b7293f76eb8331d09162cc21e9..51cdafe01ab1579da621d01c83a6c16d1b47cd17 100644 (file)
@@ -1,8 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
 XFS Self Describing Metadata
-----------------------------
+============================
 
 Introduction
-------------
+============
 
 The largest scalability problem facing XFS is not one of algorithmic
 scalability, but of verification of the filesystem structure. Scalabilty of the
@@ -34,7 +37,7 @@ required for basic forensic analysis of the filesystem structure.
 
 
 Self Describing Metadata
-------------------------
+========================
 
 One of the problems with the current metadata format is that apart from the
 magic number in the metadata block, we have no other way of identifying what it
@@ -142,7 +145,7 @@ modification occurred between the corruption being written and when it was
 detected.
 
 Runtime Validation
-------------------
+==================
 
 Validation of self-describing metadata takes place at runtime in two places:
 
@@ -183,18 +186,18 @@ error occurs during this process, the buffer is again marked with a EFSCORRUPTED
 error for the higher layers to catch.
 
 Structures
-----------
+==========
 
-A typical on-disk structure needs to contain the following information:
+A typical on-disk structure needs to contain the following information::
 
-struct xfs_ondisk_hdr {
-        __be32  magic;         /* magic number */
-        __be32  crc;           /* CRC, not logged */
-        uuid_t  uuid;          /* filesystem identifier */
-        __be64  owner;         /* parent object */
-        __be64  blkno;         /* location on disk */
-        __be64  lsn;           /* last modification in log, not logged */
-};
+    struct xfs_ondisk_hdr {
+           __be32  magic;              /* magic number */
+           __be32  crc;                /* CRC, not logged */
+           uuid_t  uuid;               /* filesystem identifier */
+           __be64  owner;              /* parent object */
+           __be64  blkno;              /* location on disk */
+           __be64  lsn;                /* last modification in log, not logged */
+    };
 
 Depending on the metadata, this information may be part of a header structure
 separate to the metadata contents, or may be distributed through an existing
@@ -214,24 +217,24 @@ level of information is generally provided. For example:
          well. hence the additional metadata headers change the overall format
          of the metadata.
 
-A typical buffer read verifier is structured as follows:
+A typical buffer read verifier is structured as follows::
 
-#define XFS_FOO_CRC_OFF                offsetof(struct xfs_ondisk_hdr, crc)
+    #define XFS_FOO_CRC_OFF            offsetof(struct xfs_ondisk_hdr, crc)
 
-static void
-xfs_foo_read_verify(
-       struct xfs_buf  *bp)
-{
-       struct xfs_mount *mp = bp->b_mount;
+    static void
+    xfs_foo_read_verify(
+           struct xfs_buf      *bp)
+    {
+       struct xfs_mount *mp = bp->b_mount;
 
-        if ((xfs_sb_version_hascrc(&mp->m_sb) &&
-             !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
-                                       XFS_FOO_CRC_OFF)) ||
-            !xfs_foo_verify(bp)) {
-                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
-                xfs_buf_ioerror(bp, EFSCORRUPTED);
-        }
-}
+           if ((xfs_sb_version_hascrc(&mp->m_sb) &&
+               !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+                                           XFS_FOO_CRC_OFF)) ||
+               !xfs_foo_verify(bp)) {
+                   XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+                   xfs_buf_ioerror(bp, EFSCORRUPTED);
+           }
+    }
 
 The code ensures that the CRC is only checked if the filesystem has CRCs enabled
 by checking the superblock of the feature bit, and then if the CRC verifies OK
@@ -239,83 +242,83 @@ by checking the superblock of the feature bit, and then if the CRC verifies OK
 
 The verifier function will take a couple of different forms, depending on
 whether the magic number can be used to determine the format of the block. In
-the case it can't, the code is structured as follows:
+the case it can't, the code is structured as follows::
 
-static bool
-xfs_foo_verify(
-       struct xfs_buf          *bp)
-{
-        struct xfs_mount       *mp = bp->b_mount;
-        struct xfs_ondisk_hdr  *hdr = bp->b_addr;
+    static bool
+    xfs_foo_verify(
+           struct xfs_buf              *bp)
+    {
+           struct xfs_mount    *mp = bp->b_mount;
+           struct xfs_ondisk_hdr       *hdr = bp->b_addr;
 
-        if (hdr->magic != cpu_to_be32(XFS_FOO_MAGIC))
-                return false;
+           if (hdr->magic != cpu_to_be32(XFS_FOO_MAGIC))
+                   return false;
 
-        if (!xfs_sb_version_hascrc(&mp->m_sb)) {
-               if (!uuid_equal(&hdr->uuid, &mp->m_sb.sb_uuid))
-                       return false;
-               if (bp->b_bn != be64_to_cpu(hdr->blkno))
-                       return false;
-               if (hdr->owner == 0)
-                       return false;
-       }
+           if (!xfs_sb_version_hascrc(&mp->m_sb)) {
+                   if (!uuid_equal(&hdr->uuid, &mp->m_sb.sb_uuid))
+                           return false;
+                   if (bp->b_bn != be64_to_cpu(hdr->blkno))
+                           return false;
+                   if (hdr->owner == 0)
+                           return false;
+           }
 
-       /* object specific verification checks here */
+           /* object specific verification checks here */
 
-        return true;
-}
+           return true;
+    }
 
 If there are different magic numbers for the different formats, the verifier
-will look like:
-
-static bool
-xfs_foo_verify(
-       struct xfs_buf          *bp)
-{
-        struct xfs_mount       *mp = bp->b_mount;
-        struct xfs_ondisk_hdr  *hdr = bp->b_addr;
-
-        if (hdr->magic == cpu_to_be32(XFS_FOO_CRC_MAGIC)) {
-               if (!uuid_equal(&hdr->uuid, &mp->m_sb.sb_uuid))
-                       return false;
-               if (bp->b_bn != be64_to_cpu(hdr->blkno))
-                       return false;
-               if (hdr->owner == 0)
-                       return false;
-       } else if (hdr->magic != cpu_to_be32(XFS_FOO_MAGIC))
-               return false;
-
-       /* object specific verification checks here */
-
-        return true;
-}
+will look like::
+
+    static bool
+    xfs_foo_verify(
+           struct xfs_buf              *bp)
+    {
+           struct xfs_mount    *mp = bp->b_mount;
+           struct xfs_ondisk_hdr       *hdr = bp->b_addr;
+
+           if (hdr->magic == cpu_to_be32(XFS_FOO_CRC_MAGIC)) {
+                   if (!uuid_equal(&hdr->uuid, &mp->m_sb.sb_uuid))
+                           return false;
+                   if (bp->b_bn != be64_to_cpu(hdr->blkno))
+                           return false;
+                   if (hdr->owner == 0)
+                           return false;
+           } else if (hdr->magic != cpu_to_be32(XFS_FOO_MAGIC))
+                   return false;
+
+           /* object specific verification checks here */
+
+           return true;
+    }
 
 Write verifiers are very similar to the read verifiers, they just do things in
-the opposite order to the read verifiers. A typical write verifier:
+the opposite order to the read verifiers. A typical write verifier::
 
-static void
-xfs_foo_write_verify(
-       struct xfs_buf  *bp)
-{
-       struct xfs_mount        *mp = bp->b_mount;
-       struct xfs_buf_log_item *bip = bp->b_fspriv;
+    static void
+    xfs_foo_write_verify(
+           struct xfs_buf      *bp)
+    {
+           struct xfs_mount    *mp = bp->b_mount;
+           struct xfs_buf_log_item     *bip = bp->b_fspriv;
 
-       if (!xfs_foo_verify(bp)) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-               return;
-       }
+           if (!xfs_foo_verify(bp)) {
+                   XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+                   xfs_buf_ioerror(bp, EFSCORRUPTED);
+                   return;
+           }
 
-       if (!xfs_sb_version_hascrc(&mp->m_sb))
-               return;
+           if (!xfs_sb_version_hascrc(&mp->m_sb))
+                   return;
 
 
-       if (bip) {
-               struct xfs_ondisk_hdr   *hdr = bp->b_addr;
-               hdr->lsn = cpu_to_be64(bip->bli_item.li_lsn);
-       }
-       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_FOO_CRC_OFF);
-}
+           if (bip) {
+                   struct xfs_ondisk_hdr       *hdr = bp->b_addr;
+                   hdr->lsn = cpu_to_be64(bip->bli_item.li_lsn);
+           }
+           xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_FOO_CRC_OFF);
+    }
 
 This will verify the internal structure of the metadata before we go any
 further, detecting corruptions that have occurred as the metadata has been
@@ -324,7 +327,7 @@ update the LSN field (when it was last modified) and calculate the CRC on the
 metadata. Once this is done, we can issue the IO.
 
 Inodes and Dquots
------------------
+=================
 
 Inodes and dquots are special snowflakes. They have per-object CRC and
 self-identifiers, but they are packed so that there are multiple objects per
@@ -347,4 +350,3 @@ XXX: inode unlinked list modification doesn't recalculate the inode CRC! None of
 the unlinked list modifications check or update CRCs, neither during unlink nor
 log recovery. So, it's gone unnoticed until now. This won't matter immediately -
 repair will probably complain about it - but it needs to be fixed.
-
diff --git a/Documentation/hwmon/amd_energy.rst b/Documentation/hwmon/amd_energy.rst
new file mode 100644 (file)
index 0000000..f8288ed
--- /dev/null
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver amd_energy
+==========================
+
+Supported chips:
+
+* AMD Family 17h Processors
+
+  Prefix: 'amd_energy'
+
+  Addresses used:  RAPL MSRs
+
+  Datasheets:
+
+  - Processor Programming Reference (PPR) for AMD Family 17h Model 01h, Revision B1 Processors
+
+       https://developer.amd.com/wp-content/resources/55570-B1_PUB.zip
+
+  - Preliminary Processor Programming Reference (PPR) for AMD Family 17h Model 31h, Revision B0 Processors
+
+       https://developer.amd.com/wp-content/resources/56176_ppr_Family_17h_Model_71h_B0_pub_Rev_3.06.zip
+
+Author: Naveen Krishna Chatradhi <nchatrad@amd.com>
+
+Description
+-----------
+
+The Energy driver exposes the energy counters that are
+reported via the Running Average Power Limit (RAPL)
+Model-specific Registers (MSRs) via the hardware monitor
+(HWMON) sysfs interface.
+
+1. Power, Energy and Time Units
+   MSR_RAPL_POWER_UNIT/ C001_0299:
+   shared with all cores in the socket
+
+2. Energy consumed by each Core
+   MSR_CORE_ENERGY_STATUS/ C001_029A:
+   32-bitRO, Accumulator, core-level power reporting
+
+3. Energy consumed by Socket
+   MSR_PACKAGE_ENERGY_STATUS/ C001_029B:
+   32-bitRO, Accumulator, socket-level power reporting,
+   shared with all cores in socket
+
+These registers are updated every 1ms and cleared on
+reset of the system.
+
+Note: If SMT is enabled, Linux enumerates all threads as cpus.
+Since, the energy status registers are accessed at core level,
+reading those registers from the sibling threads would result
+in duplicate values. Hence, energy counter entries are not
+populated for the siblings.
+
+Energy Caluclation
+------------------
+
+Energy information (in Joules) is based on the multiplier,
+1/2^ESU; where ESU is an unsigned integer read from
+MSR_RAPL_POWER_UNIT register. Default value is 10000b,
+indicating energy status unit is 15.3 micro-Joules increment.
+
+Reported values are scaled as per the formula
+
+scaled value = ((1/2^ESU) * (Raw value) * 1000000UL) in uJoules
+
+Users calculate power for a given domain by calculating
+       dEnergy/dTime for that domain.
+
+Energy accumulation
+--------------------------
+
+Current, Socket energy status register is 32bit, assuming a 240W
+2P system, the register would wrap around in
+
+       2^32*15.3 e-6/240 * 2 = 547.60833024 secs to wrap(~9 mins)
+
+The Core energy register may wrap around after several days.
+
+To improve the wrap around time, a kernel thread is implemented
+to accumulate the socket energy counters and one core energy counter
+per run to a respective 64-bit counter. The kernel thread starts
+running during probe, wakes up every 100secs and stops running
+when driver is removed.
+
+A socket and core energy read would return the current register
+value added to the respective energy accumulator.
+
+Sysfs attributes
+----------------
+
+=============== ========  =====================================
+Attribute      Label     Description
+===============        ========  =====================================
+
+* For index N between [1] and [nr_cpus]
+
+===============        ========  ======================================
+energy[N]_input EcoreX   Core Energy   X = [0] to [nr_cpus - 1]
+                         Measured input core energy
+===============        ========  ======================================
+
+* For N between [nr_cpus] and [nr_cpus + nr_socks]
+
+===============        ========  ======================================
+energy[N]_input EsocketX  Socket Energy X = [0] to [nr_socks -1]
+                         Measured input socket energy
+=============== ========  ======================================
diff --git a/Documentation/hwmon/bt1-pvt.rst b/Documentation/hwmon/bt1-pvt.rst
new file mode 100644 (file)
index 0000000..cbb0c06
--- /dev/null
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver bt1-pvt
+=====================
+
+Supported chips:
+
+  * Baikal-T1 PVT sensor (in SoC)
+
+    Prefix: 'bt1-pvt'
+
+    Addresses scanned: -
+
+    Datasheet: Provided by BAIKAL ELECTRONICS upon request and under NDA
+
+Authors:
+    Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+    Serge Semin <Sergey.Semin@baikalelectronics.ru>
+
+Description
+-----------
+
+This driver implements support for the hardware monitoring capabilities of the
+embedded into Baikal-T1 process, voltage and temperature sensors. PVT IP-core
+consists of one temperature and four voltage sensors, which can be used to
+monitor the chip internal environment like heating, supply voltage and
+transistors performance. The driver can optionally provide the hwmon alarms
+for each sensor the PVT controller supports. The alarms functionality is made
+compile-time configurable due to the hardware interface implementation
+peculiarity, which is connected with an ability to convert data from only one
+sensor at a time. Additional limitation is that the controller performs the
+thresholds checking synchronously with the data conversion procedure. Due to
+these in order to have the hwmon alarms automatically detected the driver code
+must switch from one sensor to another, read converted data and manually check
+the threshold status bits. Depending on the measurements timeout settings
+(update_interval sysfs node value) this design may cause additional burden on
+the system performance. So in case if alarms are unnecessary in your system
+design it's recommended to have them disabled to prevent the PVT IRQs being
+periodically raised to get the data cache/alarms status up to date. By default
+in alarm-less configuration the data conversion is performed by the driver
+on demand when read operation is requested via corresponding _input-file.
+
+Temperature Monitoring
+----------------------
+
+Temperature is measured with 10-bit resolution and reported in millidegree
+Celsius. The driver performs all the scaling by itself therefore reports true
+temperatures that don't need any user-space adjustments. While the data
+translation formulae isn't linear, which gives us non-linear discreteness,
+it's close to one, but giving a bit better accuracy for higher temperatures.
+The temperature input is mapped as follows (the last column indicates the input
+ranges)::
+
+       temp1: CPU embedded diode       -48.38C - +147.438C
+
+In case if the alarms kernel config is enabled in the driver the temperature input
+has associated min and max limits which trigger an alarm when crossed.
+
+Voltage Monitoring
+------------------
+
+The voltage inputs are also sampled with 10-bit resolution and reported in
+millivolts. But in this case the data translation formulae is linear, which
+provides a constant measurements discreteness. The data scaling is also
+performed by the driver, so returning true millivolts. The voltage inputs are
+mapped as follows (the last column indicates the input ranges)::
+
+       in0: VDD                (processor core)                0.62V - 1.168V
+       in1: Low-Vt             (low voltage threshold)         0.62V - 1.168V
+       in2: High-Vt            (high voltage threshold)        0.62V - 1.168V
+       in3: Standard-Vt        (standard voltage threshold)    0.62V - 1.168V
+
+In case if the alarms config is enabled in the driver the voltage inputs
+have associated min and max limits which trigger an alarm when crossed.
+
+Sysfs Attributes
+----------------
+
+Following is a list of all sysfs attributes that the driver provides, their
+permissions and a short description:
+
+=============================== ======= =======================================
+Name                           Perm    Description
+=============================== ======= =======================================
+update_interval                        RW      Measurements update interval per
+                                       sensor.
+temp1_type                     RO      Sensor type (always 1 as CPU embedded
+                                       diode).
+temp1_label                    RO      CPU Core Temperature sensor.
+temp1_input                    RO      Measured temperature in millidegree
+                                       Celsius.
+temp1_min                      RW      Low limit for temp input.
+temp1_max                      RW      High limit for temp input.
+temp1_min_alarm                        RO      Temperature input alarm. Returns 1 if
+                                       temperature input went below min limit,
+                                       0 otherwise.
+temp1_max_alarm                        RO      Temperature input alarm. Returns 1 if
+                                       temperature input went above max limit,
+                                       0 otherwise.
+temp1_offset                   RW      Temperature offset in millidegree
+                                       Celsius which is added to the
+                                       temperature reading by the chip. It can
+                                       be used to manually adjust the
+                                       temperature measurements within 7.130
+                                       degrees Celsius.
+in[0-3]_label                  RO      CPU Voltage sensor (either core or
+                                       low/high/standard thresholds).
+in[0-3]_input                  RO      Measured voltage in millivolts.
+in[0-3]_min                    RW      Low limit for voltage input.
+in[0-3]_max                    RW      High limit for voltage input.
+in[0-3]_min_alarm              RO      Voltage input alarm. Returns 1 if
+                                       voltage input went below min limit,
+                                       0 otherwise.
+in[0-3]_max_alarm              RO      Voltage input alarm. Returns 1 if
+                                       voltage input went above max limit,
+                                       0 otherwise.
+=============================== ======= =======================================
diff --git a/Documentation/hwmon/gsc-hwmon.rst b/Documentation/hwmon/gsc-hwmon.rst
new file mode 100644 (file)
index 0000000..ffac392
--- /dev/null
@@ -0,0 +1,53 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver gsc-hwmon
+=======================
+
+Supported chips: Gateworks GSC
+Datasheet: http://trac.gateworks.com/wiki/gsc
+Author: Tim Harvey <tharvey@gateworks.com>
+
+Description:
+------------
+
+This driver supports hardware monitoring for the temperature sensor,
+various ADC's connected to the GSC, and optional FAN controller available
+on some boards.
+
+
+Voltage Monitoring
+------------------
+
+The voltage inputs are scaled either internally or by the driver depending
+on the GSC version and firmware. The values returned by the driver do not need
+further scaling. The voltage input labels provide the voltage rail name:
+
+inX_input                  Measured voltage (mV).
+inX_label                  Name of voltage rail.
+
+
+Temperature Monitoring
+----------------------
+
+Temperatures are measured with 12-bit or 10-bit resolution and are scaled
+either internally or by the driver depending on the GSC version and firmware.
+The values returned by the driver reflect millidegree Celcius:
+
+tempX_input                Measured temperature.
+tempX_label                Name of temperature input.
+
+
+PWM Output Control
+------------------
+
+The GSC features 1 PWM output that operates in automatic mode where the
+PWM value will be scalled depending on 6 temperature boundaries.
+The tempeature boundaries are read-write and in millidegree Celcius and the
+read-only PWM values range from 0 (off) to 255 (full speed).
+Fan speed will be set to minimum (off) when the temperature sensor reads
+less than pwm1_auto_point1_temp and maximum when the temperature sensor
+equals or exceeds pwm1_auto_point6_temp.
+
+pwm1_auto_point[1-6]_pwm       PWM value.
+pwm1_auto_point[1-6]_temp      Temperature boundary.
+
index 94b9a260c5185579b30c67f80c3feb2d6ebfef92..ed81f541633181637742961f11cc8437acda5143 100644 (file)
@@ -99,6 +99,25 @@ Sysfs entries for ina226, ina230 and ina231 only
 ------------------------------------------------
 
 ======================= ====================================================
+in0_lcrit              Critical low shunt voltage
+in0_crit               Critical high shunt voltage
+in0_lcrit_alarm                Shunt voltage critical low alarm
+in0_crit_alarm         Shunt voltage critical high alarm
+in1_lcrit              Critical low bus voltage
+in1_crit               Critical high bus voltage
+in1_lcrit_alarm                Bus voltage critical low alarm
+in1_crit_alarm         Bus voltage critical high alarm
+power1_crit            Critical high power
+power1_crit_alarm      Power critical high alarm
 update_interval                data conversion time; affects number of samples used
                        to average results for shunt and bus voltages.
 ======================= ====================================================
+
+.. note::
+
+   - Configure `shunt_resistor` before configure `power1_crit`, because power
+     value is calculated based on `shunt_resistor` set.
+   - Because of the underlying register implementation, only one `*crit` setting
+     and its `alarm` can be active. Writing to one `*crit` setting clears other
+     `*crit` settings and alarms. Writing 0 to any `*crit` setting clears all
+     `*crit` settings and alarms.
index 8ef62fd3978798673e930f871449971643a4a1d5..005bf9e124bb9d12906871fd3595d862670ef836 100644 (file)
@@ -39,10 +39,12 @@ Hardware Monitoring Kernel Drivers
    adt7470
    adt7475
    amc6821
+   amd_energy
    asb100
    asc7621
    aspeed-pwm-tacho
    bel-pfe
+   bt1-pvt
    coretemp
    da9052
    da9055
@@ -60,6 +62,7 @@ Hardware Monitoring Kernel Drivers
    ftsteutates
    g760a
    g762
+   gsc-hwmon
    gl518sm
    hih6130
    ibmaem
@@ -106,6 +109,7 @@ Hardware Monitoring Kernel Drivers
    max16064
    max16065
    max1619
+   max16601
    max1668
    max197
    max20730
index 953315987c06e87999113a2b7868dd4223e7b4d1..78dfc01b47a23b0e27e113bd56788abf12333466 100644 (file)
@@ -123,6 +123,18 @@ Supported chips:
 
               http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
 
+  * Maxim MAX6654
+
+    Prefix: 'max6654'
+
+    Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
+
+                          0x4c, 0x4d and 0x4e
+
+    Datasheet: Publicly available at the Maxim website
+
+              https://www.maximintegrated.com/en/products/sensors/MAX6654.html
+
   * Maxim MAX6657
 
     Prefix: 'max6657'
@@ -301,6 +313,13 @@ ADT7461, ADT7461A, NCT1008:
   * Extended temperature range (breaks compatibility)
   * Lower resolution for remote temperature
 
+MAX6654:
+  * Better local resolution
+  * Selectable address
+  * Remote sensor type selection
+  * Extended temperature range
+  * Extended resolution only available when conversion rate <= 1 Hz
+
 MAX6657 and MAX6658:
   * Better local resolution
   * Remote sensor type selection
@@ -336,8 +355,8 @@ SA56004X:
 
 All temperature values are given in degrees Celsius. Resolution
 is 1.0 degree for the local temperature, 0.125 degree for the remote
-temperature, except for the MAX6657, MAX6658 and MAX6659 which have a
-resolution of 0.125 degree for both temperatures.
+temperature, except for the MAX6654, MAX6657, MAX6658 and MAX6659 which have
+resolution of 0.125 degree for both temperatures.
 
 Each sensor has its own high and low limits, plus a critical limit.
 Additionally, there is a relative hysteresis value common to both critical
diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst
new file mode 100644 (file)
index 0000000..346e746
--- /dev/null
@@ -0,0 +1,159 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver max16601
+======================
+
+Supported chips:
+
+  * Maxim MAX16601
+
+    Prefix: 'max16601'
+
+    Addresses scanned: -
+
+    Datasheet: Not published
+
+Author: Guenter Roeck <linux@roeck-us.net>
+
+
+Description
+-----------
+
+This driver supports the MAX16601 VR13.HC Dual-Output Voltage Regulator
+Chipset.
+
+The driver is a client driver to the core PMBus driver.
+Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported.
+
+======================= =======================================================
+in1_label              "vin1"
+in1_input              VCORE input voltage.
+in1_alarm              Input voltage alarm.
+
+in2_label              "vout1"
+in2_input              VCORE output voltage.
+in2_alarm              Output voltage alarm.
+
+curr1_label            "iin1"
+curr1_input            VCORE input current, derived from duty cycle and output
+                       current.
+curr1_max              Maximum input current.
+curr1_max_alarm                Current high alarm.
+
+curr2_label            "iin1.0"
+curr2_input            VCORE phase 0 input current.
+
+curr3_label            "iin1.1"
+curr3_input            VCORE phase 1 input current.
+
+curr4_label            "iin1.2"
+curr4_input            VCORE phase 2 input current.
+
+curr5_label            "iin1.3"
+curr5_input            VCORE phase 3 input current.
+
+curr6_label            "iin1.4"
+curr6_input            VCORE phase 4 input current.
+
+curr7_label            "iin1.5"
+curr7_input            VCORE phase 5 input current.
+
+curr8_label            "iin1.6"
+curr8_input            VCORE phase 6 input current.
+
+curr9_label            "iin1.7"
+curr9_input            VCORE phase 7 input current.
+
+curr10_label           "iin2"
+curr10_input           VCORE input current, derived from sensor element.
+
+curr11_label           "iin3"
+curr11_input           VSA input current.
+
+curr12_label           "iout1"
+curr12_input           VCORE output current.
+curr12_crit            Critical output current.
+curr12_crit_alarm      Output current critical alarm.
+curr12_max             Maximum output current.
+curr12_max_alarm       Output current high alarm.
+
+curr13_label           "iout1.0"
+curr13_input           VCORE phase 0 output current.
+
+curr14_label           "iout1.1"
+curr14_input           VCORE phase 1 output current.
+
+curr15_label           "iout1.2"
+curr15_input           VCORE phase 2 output current.
+
+curr16_label           "iout1.3"
+curr16_input           VCORE phase 3 output current.
+
+curr17_label           "iout1.4"
+curr17_input           VCORE phase 4 output current.
+
+curr18_label           "iout1.5"
+curr18_input           VCORE phase 5 output current.
+
+curr19_label           "iout1.6"
+curr19_input           VCORE phase 6 output current.
+
+curr20_label           "iout1.7"
+curr20_input           VCORE phase 7 output current.
+
+curr21_label           "iout3"
+curr21_input           VSA output current.
+curr21_highest         Historical maximum VSA output current.
+curr21_reset_history   Write any value to reset curr21_highest.
+curr21_crit            Critical output current.
+curr21_crit_alarm      Output current critical alarm.
+curr21_max             Maximum output current.
+curr21_max_alarm       Output current high alarm.
+
+power1_label           "pin1"
+power1_input           Input power, derived from duty cycle and output current.
+power1_alarm           Input power alarm.
+
+power2_label           "pin2"
+power2_input           Input power, derived from input current sensor.
+
+power3_label           "pout"
+power3_input           Output power.
+
+temp1_input            VCORE temperature.
+temp1_crit             Critical high temperature.
+temp1_crit_alarm       Chip temperature critical high alarm.
+temp1_max              Maximum temperature.
+temp1_max_alarm                Chip temperature high alarm.
+
+temp2_input            TSENSE_0 temperature
+temp3_input            TSENSE_1 temperature
+temp4_input            TSENSE_2 temperature
+temp5_input            TSENSE_3 temperature
+
+temp6_input            VSA temperature.
+temp6_crit             Critical high temperature.
+temp6_crit_alarm       Chip temperature critical high alarm.
+temp6_max              Maximum temperature.
+temp6_max_alarm                Chip temperature high alarm.
+======================= =======================================================
similarity index 99%
rename from Documentation/i2c/i2c.svg
rename to Documentation/i2c/i2c_bus.svg
index 5979405ad1c3e1e4dec743c17d1bcf3a7d8e6a12..3170de976373cbd1a4fdcb98a702a24bbc83eb20 100644 (file)
@@ -9,7 +9,7 @@
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   sodipodi:docname="i2c.svg"
+   sodipodi:docname="i2c_bus.svg"
    inkscape:version="0.92.3 (2405546, 2018-03-11)"
    version="1.1"
    id="svg2"
index ce7230025b33d27392e08619157483cf08254bda..136c4e333be771d91826c839cdbcf5d138a9ccf2 100644 (file)
@@ -34,7 +34,7 @@ Terminology
 Using the terminology from the official documentation, the I2C bus connects
 one or more *master* chips and one or more *slave* chips.
 
-.. kernel-figure::  i2c.svg
+.. kernel-figure::  i2c_bus.svg
    :alt:    Simple I2C bus with one master and 3 slaves
 
    Simple I2C bus
index 39bf94484a15b4651c6cd455712f0af3cac783eb..6bbbbe4f73efc487992ef9e3a5ccf7c6bf144b7e 100644 (file)
@@ -7,7 +7,7 @@ IRQ affinity on IA64 platforms
 
 By writing to /proc/irq/IRQ#/smp_affinity the interrupt routing can be
 controlled. The behavior on IA64 platforms is slightly different from
-that described in Documentation/IRQ-affinity.txt for i386 systems.
+that described in Documentation/core-api/irq/irq-affinity.rst for i386 systems.
 
 Because of the usage of SAPIC mode and physical destination mode the
 IRQ target is one particular CPU and cannot be a mask of several
index ecbfdb3afef7e6b470f41e3684fa18550c965ec4..6e38cbbd298192e84502f094a96e2faf951b7709 100644 (file)
@@ -9,7 +9,7 @@ Configfs is a filesystem-based manager of kernel objects. IIO uses some
 objects that could be easily configured using configfs (e.g.: devices,
 triggers).
 
-See Documentation/filesystems/configfs/configfs.txt for more information
+See Documentation/filesystems/configfs.rst for more information
 about how configfs works.
 
 2. Usage
index 5d6800a723dc6583f05fb66d907e08cb77fdf926..d785878cad65c22ce246c2c7c84cbe26f5fd8707 100644 (file)
@@ -16,6 +16,13 @@ locking
     rt-mutex
     spinlocks
     ww-mutex-design
+    preempt-locking
+    pi-futex
+    futex-requeue-pi
+    hwspinlock
+    percpu-rw-semaphore
+    robust-futexes
+    robust-futex-ABI
 
 .. only::  subproject and html
 
index 5bcb99ba7bd9e5fbac823cb13e6daa79ed751f1c..8012a74555e7f892f83647d33d8c8e7d956008e4 100644 (file)
@@ -110,7 +110,7 @@ stutter
                  same period of time.  Defaults to "stutter=5", so as
                  to run and pause for (roughly) five-second intervals.
                  Specifying "stutter=0" causes the test to run continuously
-                 without pausing, which is the old default behavior.
+                 without pausing.
 
 shuffle_interval
                  The number of seconds to keep the test threads affinitied
index 09f45ce38d262766f7261944df0527c145926a01..1b577a8bf9829518c3e8f68aa90c7156e47b0c71 100644 (file)
@@ -13,6 +13,7 @@ The kernel provides a variety of locking primitives which can be divided
 into two categories:
 
  - Sleeping locks
+ - CPU local locks
  - Spinning locks
 
 This document conceptually describes these lock types and provides rules
@@ -44,9 +45,23 @@ Sleeping lock types:
 
 On PREEMPT_RT kernels, these lock types are converted to sleeping locks:
 
+ - local_lock
  - spinlock_t
  - rwlock_t
 
+
+CPU local locks
+---------------
+
+ - local_lock
+
+On non-PREEMPT_RT kernels, local_lock functions are wrappers around
+preemption and interrupt disabling primitives. Contrary to other locking
+mechanisms, disabling preemption or interrupts are pure CPU local
+concurrency control mechanisms and not suited for inter-CPU concurrency
+control.
+
+
 Spinning locks
 --------------
 
@@ -67,6 +82,7 @@ can have suffixes which apply further protections:
  _irqsave/restore()   Save and disable / restore interrupt disabled state
  ===================  ====================================================
 
+
 Owner semantics
 ===============
 
@@ -139,6 +155,56 @@ implementation, thus changing the fairness:
  writer from starving readers.
 
 
+local_lock
+==========
+
+local_lock provides a named scope to critical sections which are protected
+by disabling preemption or interrupts.
+
+On non-PREEMPT_RT kernels local_lock operations map to the preemption and
+interrupt disabling and enabling primitives:
+
+ =========================== ======================
+ local_lock(&llock)          preempt_disable()
+ local_unlock(&llock)        preempt_enable()
+ local_lock_irq(&llock)      local_irq_disable()
+ local_unlock_irq(&llock)    local_irq_enable()
+ local_lock_save(&llock)     local_irq_save()
+ local_lock_restore(&llock)  local_irq_save()
+ =========================== ======================
+
+The named scope of local_lock has two advantages over the regular
+primitives:
+
+  - The lock name allows static analysis and is also a clear documentation
+    of the protection scope while the regular primitives are scopeless and
+    opaque.
+
+  - If lockdep is enabled the local_lock gains a lockmap which allows to
+    validate the correctness of the protection. This can detect cases where
+    e.g. a function using preempt_disable() as protection mechanism is
+    invoked from interrupt or soft-interrupt context. Aside of that
+    lockdep_assert_held(&llock) works as with any other locking primitive.
+
+local_lock and PREEMPT_RT
+-------------------------
+
+PREEMPT_RT kernels map local_lock to a per-CPU spinlock_t, thus changing
+semantics:
+
+  - All spinlock_t changes also apply to local_lock.
+
+local_lock usage
+----------------
+
+local_lock should be used in situations where disabling preemption or
+interrupts is the appropriate form of concurrency control to protect
+per-CPU data structures on a non PREEMPT_RT kernel.
+
+local_lock is not suitable to protect against preemption or interrupts on a
+PREEMPT_RT kernel due to the PREEMPT_RT specific spinlock_t semantics.
+
+
 raw_spinlock_t and spinlock_t
 =============================
 
@@ -258,10 +324,82 @@ implementation, thus changing semantics:
 PREEMPT_RT caveats
 ==================
 
+local_lock on RT
+----------------
+
+The mapping of local_lock to spinlock_t on PREEMPT_RT kernels has a few
+implications. For example, on a non-PREEMPT_RT kernel the following code
+sequence works as expected::
+
+  local_lock_irq(&local_lock);
+  raw_spin_lock(&lock);
+
+and is fully equivalent to::
+
+   raw_spin_lock_irq(&lock);
+
+On a PREEMPT_RT kernel this code sequence breaks because local_lock_irq()
+is mapped to a per-CPU spinlock_t which neither disables interrupts nor
+preemption. The following code sequence works perfectly correct on both
+PREEMPT_RT and non-PREEMPT_RT kernels::
+
+  local_lock_irq(&local_lock);
+  spin_lock(&lock);
+
+Another caveat with local locks is that each local_lock has a specific
+protection scope. So the following substitution is wrong::
+
+  func1()
+  {
+    local_irq_save(flags);    -> local_lock_irqsave(&local_lock_1, flags);
+    func3();
+    local_irq_restore(flags); -> local_lock_irqrestore(&local_lock_1, flags);
+  }
+
+  func2()
+  {
+    local_irq_save(flags);    -> local_lock_irqsave(&local_lock_2, flags);
+    func3();
+    local_irq_restore(flags); -> local_lock_irqrestore(&local_lock_2, flags);
+  }
+
+  func3()
+  {
+    lockdep_assert_irqs_disabled();
+    access_protected_data();
+  }
+
+On a non-PREEMPT_RT kernel this works correctly, but on a PREEMPT_RT kernel
+local_lock_1 and local_lock_2 are distinct and cannot serialize the callers
+of func3(). Also the lockdep assert will trigger on a PREEMPT_RT kernel
+because local_lock_irqsave() does not disable interrupts due to the
+PREEMPT_RT-specific semantics of spinlock_t. The correct substitution is::
+
+  func1()
+  {
+    local_irq_save(flags);    -> local_lock_irqsave(&local_lock, flags);
+    func3();
+    local_irq_restore(flags); -> local_lock_irqrestore(&local_lock, flags);
+  }
+
+  func2()
+  {
+    local_irq_save(flags);    -> local_lock_irqsave(&local_lock, flags);
+    func3();
+    local_irq_restore(flags); -> local_lock_irqrestore(&local_lock, flags);
+  }
+
+  func3()
+  {
+    lockdep_assert_held(&local_lock);
+    access_protected_data();
+  }
+
+
 spinlock_t and rwlock_t
 -----------------------
 
-These changes in spinlock_t and rwlock_t semantics on PREEMPT_RT kernels
+The changes in spinlock_t and rwlock_t semantics on PREEMPT_RT kernels
 have a few implications.  For example, on a non-PREEMPT_RT kernel the
 following code sequence works as expected::
 
@@ -282,9 +420,61 @@ local_lock mechanism.  Acquiring the local_lock pins the task to a CPU,
 allowing things like per-CPU interrupt disabled locks to be acquired.
 However, this approach should be used only where absolutely necessary.
 
+A typical scenario is protection of per-CPU variables in thread context::
 
-raw_spinlock_t
---------------
+  struct foo *p = get_cpu_ptr(&var1);
+
+  spin_lock(&p->lock);
+  p->count += this_cpu_read(var2);
+
+This is correct code on a non-PREEMPT_RT kernel, but on a PREEMPT_RT kernel
+this breaks. The PREEMPT_RT-specific change of spinlock_t semantics does
+not allow to acquire p->lock because get_cpu_ptr() implicitly disables
+preemption. The following substitution works on both kernels::
+
+  struct foo *p;
+
+  migrate_disable();
+  p = this_cpu_ptr(&var1);
+  spin_lock(&p->lock);
+  p->count += this_cpu_read(var2);
+
+On a non-PREEMPT_RT kernel migrate_disable() maps to preempt_disable()
+which makes the above code fully equivalent. On a PREEMPT_RT kernel
+migrate_disable() ensures that the task is pinned on the current CPU which
+in turn guarantees that the per-CPU access to var1 and var2 are staying on
+the same CPU.
+
+The migrate_disable() substitution is not valid for the following
+scenario::
+
+  func()
+  {
+    struct foo *p;
+
+    migrate_disable();
+    p = this_cpu_ptr(&var1);
+    p->val = func2();
+
+While correct on a non-PREEMPT_RT kernel, this breaks on PREEMPT_RT because
+here migrate_disable() does not protect against reentrancy from a
+preempting task. A correct substitution for this case is::
+
+  func()
+  {
+    struct foo *p;
+
+    local_lock(&foo_lock);
+    p = this_cpu_ptr(&var1);
+    p->val = func2();
+
+On a non-PREEMPT_RT kernel this protects against reentrancy by disabling
+preemption. On a PREEMPT_RT kernel this is achieved by acquiring the
+underlying per-CPU spinlock.
+
+
+raw_spinlock_t on RT
+--------------------
 
 Acquiring a raw_spinlock_t disables preemption and possibly also
 interrupts, so the critical section must avoid acquiring a regular
@@ -325,22 +515,25 @@ Lock type nesting rules
 
 The most basic rules are:
 
-  - Lock types of the same lock category (sleeping, spinning) can nest
-    arbitrarily as long as they respect the general lock ordering rules to
-    prevent deadlocks.
+  - Lock types of the same lock category (sleeping, CPU local, spinning)
+    can nest arbitrarily as long as they respect the general lock ordering
+    rules to prevent deadlocks.
+
+  - Sleeping lock types cannot nest inside CPU local and spinning lock types.
 
-  - Sleeping lock types cannot nest inside spinning lock types.
+  - CPU local and spinning lock types can nest inside sleeping lock types.
 
-  - Spinning lock types can nest inside sleeping lock types.
+  - Spinning lock types can nest inside all lock types
 
 These constraints apply both in PREEMPT_RT and otherwise.
 
 The fact that PREEMPT_RT changes the lock category of spinlock_t and
-rwlock_t from spinning to sleeping means that they cannot be acquired while
-holding a raw spinlock.  This results in the following nesting ordering:
+rwlock_t from spinning to sleeping and substitutes local_lock with a
+per-CPU spinlock_t means that they cannot be acquired while holding a raw
+spinlock.  This results in the following nesting ordering:
 
   1) Sleeping locks
-  2) spinlock_t and rwlock_t
+  2) spinlock_t, rwlock_t, local_lock
   3) raw_spinlock_t and bit spinlocks
 
 Lockdep will complain if these constraints are violated, both in
index c365dc302081f00de3e2622c5140eefab58f10f7..3b5097a380e6bd90e69ce3999a15683159789076 100644 (file)
@@ -4,7 +4,7 @@ RT-mutex subsystem with PI support
 
 RT-mutexes with priority inheritance are used to support PI-futexes,
 which enable pthread_mutex_t priority inheritance attributes
-(PTHREAD_PRIO_INHERIT). [See Documentation/pi-futex.txt for more details
+(PTHREAD_PRIO_INHERIT). [See Documentation/locking/pi-futex.rst for more details
 about PI-futexes.]
 
 This technology was developed in the -rt tree and streamlined for
index 11ebe36827710e7a80347cb0318c7b244462c183..77e43c8b24b4c81ae8ee0cb9986ceb3769686888 100644 (file)
@@ -7,7 +7,7 @@ The Maintainer Entry Profile supplements the top-level process documents
 (submitting-patches, submitting drivers...) with
 subsystem/device-driver-local customs as well as details about the patch
 submission life-cycle. A contributor uses this document to level set
-their expectations and avoid common mistakes, maintainers may use these
+their expectations and avoid common mistakes; maintainers may use these
 profiles to look across subsystems for opportunities to converge on
 common practices.
 
@@ -26,7 +26,7 @@ Example questions to consider:
 - Does the subsystem have a patchwork instance? Are patchwork state
   changes notified?
 - Any bots or CI infrastructure that watches the list, or automated
-  testing feedback that the subsystem gates acceptance?
+  testing feedback that the subsystem uses to gate acceptance?
 - Git branches that are pulled into -next?
 - What branch should contributors submit against?
 - Links to any other Maintainer Entry Profiles? For example a
@@ -54,8 +54,8 @@ One of the common misunderstandings of submitters is that patches can be
 sent at any time before the merge window closes and can still be
 considered for the next -rc1. The reality is that most patches need to
 be settled in soaking in linux-next in advance of the merge window
-opening. Clarify for the submitter the key dates (in terms rc release
-week) that patches might considered for merging and when patches need to
+opening. Clarify for the submitter the key dates (in terms of -rc release
+week) that patches might be considered for merging and when patches need to
 wait for the next -rc. At a minimum:
 
 - Last -rc for new feature submissions:
@@ -70,8 +70,8 @@ wait for the next -rc. At a minimum:
 - Last -rc to merge features: Deadline for merge decisions
   Indicate to contributors the point at which an as yet un-applied patch
   set will need to wait for the NEXT+1 merge window. Of course there is no
-  obligation to ever except any given patchset, but if the review has not
-  concluded by this point the expectation the contributor should wait and
+  obligation to ever accept any given patchset, but if the review has not
+  concluded by this point the expectation is the contributor should wait and
   resubmit for the following merge window.
 
 Optional:
index e1c355e84edde3f39658f4eb3df5741f26e1552e..eaabc31342949a09b409337a17cb7654a2c5fb4f 100644 (file)
@@ -620,7 +620,7 @@ because the CPUs that the Linux kernel supports don't do writes
 until they are certain (1) that the write will actually happen, (2)
 of the location of the write, and (3) of the value to be written.
 But please carefully read the "CONTROL DEPENDENCIES" section and the
-Documentation/RCU/rcu_dereference.txt file:  The compiler can and does
+Documentation/RCU/rcu_dereference.rst file:  The compiler can and does
 break dependencies in a great many highly creative ways.
 
        CPU 1                 CPU 2
index c1dcd262891188488843821ed12a1c0037a825ca..1ecc05fbe6f41f32acc4c85c2a6ddcd91e726c6a 100644 (file)
@@ -21,4 +21,5 @@ fit into other categories.
    lis3lv02d
    max6875
    mic/index
+   uacce
    xilinx_sdfec
index f78d7bf27ff5c8957f44840eb57d4db0d840290e..8f0347b9fb3d388457aaf817cd005e2a8ae40da3 100644 (file)
@@ -81,7 +81,7 @@ of queues to IRQs can be determined from /proc/interrupts. By default,
 an IRQ may be handled on any CPU. Because a non-negligible part of packet
 processing takes place in receive interrupt handling, it is advantageous
 to spread receive interrupts between CPUs. To manually adjust the IRQ
-affinity of each interrupt see Documentation/IRQ-affinity.txt. Some systems
+affinity of each interrupt see Documentation/core-api/irq/irq-affinity.rst. Some systems
 will be running irqbalance, a daemon that dynamically optimizes IRQ
 assignments and as a result may override any manual settings.
 
@@ -160,7 +160,7 @@ can be configured for each receive queue using a sysfs file entry::
 
 This file implements a bitmap of CPUs. RPS is disabled when it is zero
 (the default), in which case packets are processed on the interrupting
-CPU. Documentation/IRQ-affinity.txt explains how CPUs are assigned to
+CPU. Documentation/core-api/irq/irq-affinity.rst explains how CPUs are assigned to
 the bitmap.
 
 
index efe37adadceafe759c47422de9cd79832dd7457b..9da748e42623f0003ef6cd53b0354e19e7c90f86 100644 (file)
@@ -4,15 +4,15 @@ LIBNVDIMM Maintainer Entry Profile
 Overview
 --------
 The libnvdimm subsystem manages persistent memory across multiple
-architectures. The mailing list, is tracked by patchwork here:
+architectures. The mailing list is tracked by patchwork here:
 https://patchwork.kernel.org/project/linux-nvdimm/list/
 ...and that instance is configured to give feedback to submitters on
 patch acceptance and upstream merge. Patches are merged to either the
-'libnvdimm-fixes', or 'libnvdimm-for-next' branch. Those branches are
+'libnvdimm-fixes' or 'libnvdimm-for-next' branch. Those branches are
 available here:
 https://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm.git/
 
-In general patches can be submitted against the latest -rc, however if
+In general patches can be submitted against the latest -rc; however, if
 the incoming code change is dependent on other pending changes then the
 patch should be based on the libnvdimm-for-next branch. However, since
 persistent memory sits at the intersection of storage and memory there
@@ -35,12 +35,12 @@ getting the test environment set up.
 
 ACPI Device Specific Methods (_DSM)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Before patches enabling for a new _DSM family will be considered it must
+Before patches enabling a new _DSM family will be considered, it must
 be assigned a format-interface-code from the NVDIMM Sub-team of the ACPI
 Specification Working Group. In general, the stance of the subsystem is
-to push back on the proliferation of NVDIMM command sets, do strongly
+to push back on the proliferation of NVDIMM command sets, so do strongly
 consider implementing support for an existing command set. See
-drivers/acpi/nfit/nfit.h for the set of support command sets.
+drivers/acpi/nfit/nfit.h for the set of supported command sets.
 
 
 Key Cycle Dates
@@ -48,7 +48,7 @@ Key Cycle Dates
 New submissions can be sent at any time, but if they intend to hit the
 next merge window they should be sent before -rc4, and ideally
 stabilized in the libnvdimm-for-next branch by -rc6. Of course if a
-patch set requires more than 2 weeks of review -rc4 is already too late
+patch set requires more than 2 weeks of review, -rc4 is already too late
 and some patches may require multiple development cycles to review.
 
 
index 0924d29636adf47a9b5a89126fbbd449c56814e3..1831e431f7259973126bf72edc5a3b6adb5d4350 100644 (file)
@@ -1004,41 +1004,39 @@ including the PCI bus type.  The flags should be set once at the driver probe
 time with the help of the dev_pm_set_driver_flags() function and they should not
 be updated directly afterwards.
 
-The DPM_FLAG_NEVER_SKIP flag prevents the PM core from using the direct-complete
-mechanism allowing device suspend/resume callbacks to be skipped if the device
-is in runtime suspend when the system suspend starts.  That also affects all of
-the ancestors of the device, so this flag should only be used if absolutely
-necessary.
-
-The DPM_FLAG_SMART_PREPARE flag instructs the PCI bus type to only return a
-positive value from pci_pm_prepare() if the ->prepare callback provided by the
+The DPM_FLAG_NO_DIRECT_COMPLETE flag prevents the PM core from using the
+direct-complete mechanism allowing device suspend/resume callbacks to be skipped
+if the device is in runtime suspend when the system suspend starts.  That also
+affects all of the ancestors of the device, so this flag should only be used if
+absolutely necessary.
+
+The DPM_FLAG_SMART_PREPARE flag causes the PCI bus type to return a positive
+value from pci_pm_prepare() only if the ->prepare callback provided by the
 driver of the device returns a positive value.  That allows the driver to opt
-out from using the direct-complete mechanism dynamically.
+out from using the direct-complete mechanism dynamically (whereas setting
+DPM_FLAG_NO_DIRECT_COMPLETE means permanent opt-out).
 
 The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's
 perspective the device can be safely left in runtime suspend during system
 suspend.  That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff()
-to skip resuming the device from runtime suspend unless there are PCI-specific
-reasons for doing that.  Also, it causes pci_pm_suspend_late/noirq(),
-pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq() to return early
-if the device remains in runtime suspend in the beginning of the "late" phase
-of the system-wide transition under way.  Moreover, if the device is in
-runtime suspend in pci_pm_resume_noirq() or pci_pm_restore_noirq(), its runtime
-power management status will be changed to "active" (as it is going to be put
-into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
-the function will set the power.direct_complete flag for it (to make the PM core
-skip the subsequent "thaw" callbacks for it) and return.
-
-Setting the DPM_FLAG_LEAVE_SUSPENDED flag means that the driver prefers the
-device to be left in suspend after system-wide transitions to the working state.
-This flag is checked by the PM core, but the PCI bus type informs the PM core
-which devices may be left in suspend from its perspective (that happens during
-the "noirq" phase of system-wide suspend and analogous transitions) and next it
-uses the dev_pm_may_skip_resume() helper to decide whether or not to return from
-pci_pm_resume_noirq() early, as the PM core will skip the remaining resume
-callbacks for the device during the transition under way and will set its
-runtime PM status to "suspended" if dev_pm_may_skip_resume() returns "true" for
-it.
+to avoid resuming the device from runtime suspend unless there are PCI-specific
+reasons for doing that.  Also, it causes pci_pm_suspend_late/noirq() and
+pci_pm_poweroff_late/noirq() to return early if the device remains in runtime
+suspend during the "late" phase of the system-wide transition under way.
+Moreover, if the device is in runtime suspend in pci_pm_resume_noirq() or
+pci_pm_restore_noirq(), its runtime PM status will be changed to "active" (as it
+is going to be put into D0 going forward).
+
+Setting the DPM_FLAG_MAY_SKIP_RESUME flag means that the driver allows its
+"noirq" and "early" resume callbacks to be skipped if the device can be left
+in suspend after a system-wide transition into the working state.  This flag is
+taken into consideration by the PM core along with the power.may_skip_resume
+status bit of the device which is set by pci_pm_suspend_noirq() in certain
+situations.  If the PM core determines that the driver's "noirq" and "early"
+resume callbacks should be skipped, the dev_pm_skip_resume() helper function
+will return "true" and that will cause pci_pm_resume_noirq() and
+pci_pm_resume_early() to return upfront without touching the device and
+executing the driver callbacks.
 
 3.2. Device Runtime Power Management
 ------------------------------------
index 572d968c5375a5bb5cb24c347d6d7d18b10dbd21..ebedb6c75db9353de32b01666dfb913136890112 100644 (file)
@@ -48,7 +48,7 @@ More details follow::
                                         |
                                         |
                                         v
-                              disable_nonboot_cpus()
+                              freeze_secondary_cpus()
                                    /* start */
                                         |
                                         v
@@ -83,7 +83,7 @@ More details follow::
                             Release cpu_add_remove_lock
                                         |
                                         v
-                       /* disable_nonboot_cpus() complete */
+                       /* freeze_secondary_cpus() complete */
                                         |
                                         v
                                    Do suspend
@@ -93,7 +93,7 @@ More details follow::
 Resuming back is likewise, with the counterparts being (in the order of
 execution during resume):
 
-* enable_nonboot_cpus() which involves::
+* thaw_secondary_cpus() which involves::
 
    |  Acquire cpu_add_remove_lock
    |  Decrease cpu_hotplug_disabled, thereby enabling regular cpu hotplug
index 920546d8132653b37c57e0c6dbc574bb9f2c34b0..d2d77057610e4f1a49b3ade79c41c664a93110ae 100644 (file)
@@ -133,6 +133,7 @@ User API
 ========
 
 1. AFU character devices
+^^^^^^^^^^^^^^^^^^^^^^^^
 
     For AFUs operating in AFU directed mode, two character device
     files will be created. /dev/cxl/afu0.0m will correspond to a
@@ -395,6 +396,7 @@ read
 
 
 2. Card character device (powerVM guest only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
     In a powerVM guest, an extra character device is created for the
     card. The device is only used to write (flash) a new image on the
index b3f3ee135dbe80dad846c843de14e974098827ee..20ea8cdee0aa168175bc26e4c9d3dae83eb391e6 100644 (file)
@@ -344,7 +344,7 @@ Here is the list of files under powerpc debugfs:
 
 
 NOTE:
-      Please refer to Documentation/filesystems/debugfs.txt on
+      Please refer to Documentation/filesystems/debugfs.rst on
       how to mount the debugfs filesystem.
 
 
index 1c3a840d06b903a9774af128fdcc30eedbccd078..a6b4a3a5bf3fd46cf51600cc3519b347aee2e1b3 100644 (file)
@@ -33,7 +33,7 @@ interface.
        to a somewhat opaque API.
 
  - If you're just exposing runtime system information, a new node in sysfs
-   (see ``Documentation/filesystems/sysfs.txt``) or the ``/proc`` filesystem may
+   (see ``Documentation/filesystems/sysfs.rst``) or the ``/proc`` filesystem may
    be more appropriate.  However, access to these mechanisms requires that the
    relevant filesystem is mounted, which might not always be the case (e.g.
    in a namespaced/sandboxed/chrooted environment).  Avoid adding any API to
index acb2f1b36350fec4eeab9a7b6129a0334f87945b..17a8e584f15f6ccfd770e934d35eb4702253166d 100644 (file)
@@ -84,15 +84,20 @@ Get a decent editor and don't leave whitespace at the end of lines.
 Coding style is all about readability and maintainability using commonly
 available tools.
 
-The limit on the length of lines is 80 columns and this is a strongly
-preferred limit.
-
-Statements longer than 80 columns will be broken into sensible chunks, unless
-exceeding 80 columns significantly increases readability and does not hide
-information. Descendants are always substantially shorter than the parent and
-are placed substantially to the right. The same applies to function headers
-with a long argument list. However, never break user-visible strings such as
-printk messages, because that breaks the ability to grep for them.
+The preferred limit on the length of a single line is 80 columns.
+
+Statements longer than 80 columns should be broken into sensible chunks,
+unless exceeding 80 columns significantly increases readability and does
+not hide information.
+
+Descendants are always substantially shorter than the parent and are
+are placed substantially to the right.  A very commonly used style
+is to align descendants to a function open parenthesis.
+
+These same rules are applied to function headers with a long argument list.
+
+However, never break user-visible strings such as printk messages because
+that breaks the ability to grep for them.
 
 
 3) Placing Braces and Spaces
index 6399d92f0b21d6db95a6c4893dde1f0d243108c8..f07c9250c3acb5afd6320639b7c1bbd06c93bc22 100644 (file)
@@ -61,6 +61,7 @@ lack of a better place.
    botching-up-ioctls
    clang-format
    ../riscv/patch-acceptance
+   unaligned-memory-access
 
 .. only::  subproject and html
 
index 8e56337d422d099bab9268810da3ac679a372e23..3f8e9d5d95c22f5e4517cc18c8b03a387f845863 100644 (file)
@@ -107,7 +107,7 @@ and elsewhere regarding submitting Linux kernel patches.
     and why.
 
 26) If any ioctl's are added by the patch, then also update
-    ``Documentation/ioctl/ioctl-number.rst``.
+    ``Documentation/userspace-api/ioctl/ioctl-number.rst``.
 
 27) If your modified source code depends on or uses any of the kernel
     APIs or features that are related to the following ``Kconfig`` symbols,
index b5c51f7c748d68b1218b872beaf9e69504cc8517..367e27ec3c5051da351fc51ba225d40a1f2d8347 100644 (file)
@@ -484,7 +484,7 @@ CARD.DOMAIN TYPE  MODE
 05.00ff     CEX5A Accelerator
 =========== ===== ============
 
-Guest2
+Guest3
 ------
 =========== ===== ============
 CARD.DOMAIN TYPE  MODE
index f7504226f445b69b1c29a3cc84b3bbc64567d1a7..5c4b7f4f0062610e3d4dd6dd8a187e5f84ff45db 100644 (file)
@@ -19,10 +19,12 @@ CPUs".
 Each scheduling domain must have one or more CPU groups (struct sched_group)
 which are organised as a circular one way linked list from the ->groups
 pointer. The union of cpumasks of these groups MUST be the same as the
-domain's span. The intersection of cpumasks from any two of these groups
-MUST be the empty set. The group pointed to by the ->groups pointer MUST
-contain the CPU to which the domain belongs. Groups may be shared among
-CPUs as they contain read only data after they have been set up.
+domain's span. The group pointed to by the ->groups pointer MUST contain the CPU
+to which the domain belongs. Groups may be shared among CPUs as they contain
+read only data after they have been set up. The intersection of cpumasks from
+any two of these groups may be non empty. If this is the case the SD_OVERLAP
+flag is set on the corresponding scheduling domain and its groups may not be
+shared between CPUs.
 
 Balancing within a sched domain occurs between groups. That is, each group
 is treated as one entity. The load of a group is defined as the sum of the
index fc503dd689a7ac924317db29352d34dc007474ae..8129405eb2cc7e469e72835b479ea1e41f6fb79e 100644 (file)
@@ -15,3 +15,4 @@ Security Documentation
    self-protection
    siphash
    tpm/index
+   digsig
index aadf47c808c09ba9e3a254d2836ca24f340e9ece..6a2a2e973080fffa7e9bba49c63e5022068c7a03 100644 (file)
@@ -35,47 +35,50 @@ desired model of security. Linus also suggested the possibility of
 migrating the Linux capabilities code into such a module.
 
 The Linux Security Modules (LSM) project was started by WireX to develop
-such a framework. LSM is a joint development effort by several security
+such a framework. LSM was a joint development effort by several security
 projects, including Immunix, SELinux, SGI and Janus, and several
 individuals, including Greg Kroah-Hartman and James Morris, to develop a
-Linux kernel patch that implements this framework. The patch is
-currently tracking the 2.4 series and is targeted for integration into
-the 2.5 development series. This technical report provides an overview
-of the framework and the example capabilities security module provided
-by the LSM kernel patch.
+Linux kernel patch that implements this framework. The work was
+incorporated in the mainstream in December of 2003. This technical
+report provides an overview of the framework and the capabilities
+security module.
 
 LSM Framework
 =============
 
-The LSM kernel patch provides a general kernel framework to support
+The LSM framework provides a general kernel framework to support
 security modules. In particular, the LSM framework is primarily focused
 on supporting access control modules, although future development is
-likely to address other security needs such as auditing. By itself, the
+likely to address other security needs such as sandboxing. By itself, the
 framework does not provide any additional security; it merely provides
-the infrastructure to support security modules. The LSM kernel patch
-also moves most of the capabilities logic into an optional security
-module, with the system defaulting to the traditional superuser logic.
+the infrastructure to support security modules. The LSM framework is
+optional, requiring `CONFIG_SECURITY` to be enabled. The capabilities
+logic is implemented as a security module.
 This capabilities module is discussed further in
 `LSM Capabilities Module`_.
 
-The LSM kernel patch adds security fields to kernel data structures and
-inserts calls to hook functions at critical points in the kernel code to
-manage the security fields and to perform access control. It also adds
-functions for registering and unregistering security modules, and adds a
-general :c:func:`security()` system call to support new system calls
-for security-aware applications.
-
-The LSM security fields are simply ``void*`` pointers. For process and
-program execution security information, security fields were added to
+The LSM framework includes security fields in kernel data structures and
+calls to hook functions at critical points in the kernel code to
+manage the security fields and to perform access control.
+It also adds functions for registering security modules.
+An interface `/sys/kernel/security/lsm` reports a comma separated list
+of security modules that are active on the system.
+
+The LSM security fields are simply ``void*`` pointers.
+The data is referred to as a blob, which may be managed by
+the framework or by the individual security modules that use it.
+Security blobs that are used by more than one security module are
+typically managed by the framework.
+For process and
+program execution security information, security fields are included in
 :c:type:`struct task_struct <task_struct>` and
-:c:type:`struct linux_binprm <linux_binprm>`. For filesystem
-security information, a security field was added to :c:type:`struct
+:c:type:`struct cred <cred>`.
+For filesystem
+security information, a security field is included in :c:type:`struct
 super_block <super_block>`. For pipe, file, and socket security
-information, security fields were added to :c:type:`struct inode
-<inode>` and :c:type:`struct file <file>`. For packet and
-network device security information, security fields were added to
-:c:type:`struct sk_buff <sk_buff>` and :c:type:`struct
-net_device <net_device>`. For System V IPC security information,
+information, security fields are included in :c:type:`struct inode
+<inode>` and :c:type:`struct file <file>`.
+For System V IPC security information,
 security fields were added to :c:type:`struct kern_ipc_perm
 <kern_ipc_perm>` and :c:type:`struct msg_msg
 <msg_msg>`; additionally, the definitions for :c:type:`struct
@@ -84,118 +87,45 @@ were moved to header files (``include/linux/msg.h`` and
 ``include/linux/shm.h`` as appropriate) to allow the security modules to
 use these definitions.
 
-Each LSM hook is a function pointer in a global table, security_ops.
-This table is a :c:type:`struct security_operations
-<security_operations>` structure as defined by
-``include/linux/security.h``. Detailed documentation for each hook is
-included in this header file. At present, this structure consists of a
-collection of substructures that group related hooks based on the kernel
-object (e.g. task, inode, file, sk_buff, etc) as well as some top-level
-hook function pointers for system operations. This structure is likely
-to be flattened in the future for performance. The placement of the hook
-calls in the kernel code is described by the "called:" lines in the
-per-hook documentation in the header file. The hook calls can also be
-easily found in the kernel code by looking for the string
-"security_ops->".
-
-Linus mentioned per-process security hooks in his original remarks as a
-possible alternative to global security hooks. However, if LSM were to
-start from the perspective of per-process hooks, then the base framework
-would have to deal with how to handle operations that involve multiple
-processes (e.g. kill), since each process might have its own hook for
-controlling the operation. This would require a general mechanism for
-composing hooks in the base framework. Additionally, LSM would still
-need global hooks for operations that have no process context (e.g.
-network input operations). Consequently, LSM provides global security
-hooks, but a security module is free to implement per-process hooks
-(where that makes sense) by storing a security_ops table in each
-process' security field and then invoking these per-process hooks from
-the global hooks. The problem of composition is thus deferred to the
-module.
-
-The global security_ops table is initialized to a set of hook functions
-provided by a dummy security module that provides traditional superuser
-logic. A :c:func:`register_security()` function (in
-``security/security.c``) is provided to allow a security module to set
-security_ops to refer to its own hook functions, and an
-:c:func:`unregister_security()` function is provided to revert
-security_ops to the dummy module hooks. This mechanism is used to set
-the primary security module, which is responsible for making the final
-decision for each hook.
-
-LSM also provides a simple mechanism for stacking additional security
-modules with the primary security module. It defines
-:c:func:`register_security()` and
-:c:func:`unregister_security()` hooks in the :c:type:`struct
-security_operations <security_operations>` structure and
-provides :c:func:`mod_reg_security()` and
-:c:func:`mod_unreg_security()` functions that invoke these hooks
-after performing some sanity checking. A security module can call these
-functions in order to stack with other modules. However, the actual
-details of how this stacking is handled are deferred to the module,
-which can implement these hooks in any way it wishes (including always
-returning an error if it does not wish to support stacking). In this
-manner, LSM again defers the problem of composition to the module.
-
-Although the LSM hooks are organized into substructures based on kernel
-object, all of the hooks can be viewed as falling into two major
+For packet and
+network device security information, security fields were added to
+:c:type:`struct sk_buff <sk_buff>` and
+:c:type:`struct scm_cookie <scm_cookie>`.
+Unlike the other security module data, the data used here is a
+32-bit integer. The security modules are required to map or otherwise
+associate these values with real security attributes.
+
+LSM hooks are maintained in lists. A list is maintained for each
+hook, and the hooks are called in the order specified by CONFIG_LSM.
+Detailed documentation for each hook is
+included in the `include/linux/lsm_hooks.h` header file.
+
+The LSM framework provides for a close approximation of
+general security module stacking. It defines
+security_add_hooks() to which each security module passes a
+:c:type:`struct security_hooks_list <security_hooks_list>`,
+which are added to the lists.
+The LSM framework does not provide a mechanism for removing hooks that
+have been registered. The SELinux security module has implemented
+a way to remove itself, however the feature has been deprecated.
+
+The hooks can be viewed as falling into two major
 categories: hooks that are used to manage the security fields and hooks
 that are used to perform access control. Examples of the first category
-of hooks include the :c:func:`alloc_security()` and
-:c:func:`free_security()` hooks defined for each kernel data
-structure that has a security field. These hooks are used to allocate
-and free security structures for kernel objects. The first category of
-hooks also includes hooks that set information in the security field
-after allocation, such as the :c:func:`post_lookup()` hook in
-:c:type:`struct inode_security_ops <inode_security_ops>`.
-This hook is used to set security information for inodes after
-successful lookup operations. An example of the second category of hooks
-is the :c:func:`permission()` hook in :c:type:`struct
-inode_security_ops <inode_security_ops>`. This hook checks
-permission when accessing an inode.
+of hooks include the security_inode_alloc() and security_inode_free()
+These hooks are used to allocate
+and free security structures for inode objects.
+An example of the second category of hooks
+is the security_inode_permission() hook.
+This hook checks permission when accessing an inode.
 
 LSM Capabilities Module
 =======================
 
-The LSM kernel patch moves most of the existing POSIX.1e capabilities
-logic into an optional security module stored in the file
-``security/capability.c``. This change allows users who do not want to
-use capabilities to omit this code entirely from their kernel, instead
-using the dummy module for traditional superuser logic or any other
-module that they desire. This change also allows the developers of the
-capabilities logic to maintain and enhance their code more freely,
-without needing to integrate patches back into the base kernel.
-
-In addition to moving the capabilities logic, the LSM kernel patch could
-move the capability-related fields from the kernel data structures into
-the new security fields managed by the security modules. However, at
-present, the LSM kernel patch leaves the capability fields in the kernel
-data structures. In his original remarks, Linus suggested that this
-might be preferable so that other security modules can be easily stacked
-with the capabilities module without needing to chain multiple security
-structures on the security field. It also avoids imposing extra overhead
-on the capabilities module to manage the security fields. However, the
-LSM framework could certainly support such a move if it is determined to
-be desirable, with only a few additional changes described below.
-
-At present, the capabilities logic for computing process capabilities on
-:c:func:`execve()` and :c:func:`set\*uid()`, checking
-capabilities for a particular process, saving and checking capabilities
-for netlink messages, and handling the :c:func:`capget()` and
-:c:func:`capset()` system calls have been moved into the
-capabilities module. There are still a few locations in the base kernel
-where capability-related fields are directly examined or modified, but
-the current version of the LSM patch does allow a security module to
-completely replace the assignment and testing of capabilities. These few
-locations would need to be changed if the capability-related fields were
-moved into the security field. The following is a list of known
-locations that still perform such direct examination or modification of
-capability-related fields:
-
--  ``fs/open.c``::c:func:`sys_access()`
-
--  ``fs/lockd/host.c``::c:func:`nlm_bind_host()`
-
--  ``fs/nfsd/auth.c``::c:func:`nfsd_setuser()`
-
--  ``fs/proc/array.c``::c:func:`task_cap()`
+The POSIX.1e capabilities logic is maintained as a security module
+stored in the file ``security/commoncap.c``. The capabilities
+module uses the order field of the :c:type:`lsm_info` description
+to identify it as the first security module to be registered.
+The capabilities security module does not use the general security
+blobs, unlike other modules. The reasons are historical and are
+based on overhead, complexity and performance concerns.
index 4eba68cdf0a120e274f8dcddbb08198fe28664e1..bd9363025fcbc1d843976d59985b22077585013e 100644 (file)
@@ -7,7 +7,7 @@ SipHash - a short input PRF
 SipHash is a cryptographically secure PRF -- a keyed hash function -- that
 performs very well for short inputs, hence the name. It was designed by
 cryptographers Daniel J. Bernstein and Jean-Philippe Aumasson. It is intended
-as a replacement for some uses of: `jhash`, `md5_transform`, `sha_transform`,
+as a replacement for some uses of: `jhash`, `md5_transform`, `sha1_transform`,
 and so forth.
 
 SipHash takes a secret key filled with randomly generated numbers and either
index 14e29a0ae480868b078e02e58d91f2ce7bcfe4c9..489f6626de6740374b0fdbb9f14d24243fecfdf2 100644 (file)
@@ -1,3 +1,3 @@
 docutils
-Sphinx==1.7.9
+Sphinx==2.4.4
 sphinx_rtd_theme
index ecc1e57012ef103c69d3c76de4951786c5554306..a93e52abcf46b2c49dc112112d6aa81c337a09f5 100644 (file)
@@ -1,4 +1,5 @@
 .. SPDX-License-Identifier: GPL-2.0
+
 =============================================
 CoreSight Embedded Cross Trigger (CTI & CTM).
 =============================================
index 4a2ebe0bd19b86a26d9e9a69be52254289b7acf0..f792b1959a33afee33777422d075cfce71c91030 100644 (file)
@@ -527,8 +527,8 @@ The following commands are supported:
 
   See Documentation/trace/histogram.rst for details and examples.
 
-6.3 In-kernel trace event API
------------------------------
+7. In-kernel trace event API
+============================
 
 In most cases, the command-line interface to trace events is more than
 sufficient.  Sometimes, however, applications might find the need for
@@ -560,8 +560,8 @@ following:
   - tracing synthetic events from in-kernel code
   - the low-level "dynevent_cmd" API
 
-6.3.1 Dyamically creating synthetic event definitions
------------------------------------------------------
+7.1 Dyamically creating synthetic event definitions
+---------------------------------------------------
 
 There are a couple ways to create a new synthetic event from a kernel
 module or other kernel code.
@@ -666,8 +666,8 @@ registered by calling the synth_event_gen_cmd_end() function::
 At this point, the event object is ready to be used for tracing new
 events.
 
-6.3.3 Tracing synthetic events from in-kernel code
---------------------------------------------------
+7.2 Tracing synthetic events from in-kernel code
+------------------------------------------------
 
 To trace a synthetic event, there are several options.  The first
 option is to trace the event in one call, using synth_event_trace()
@@ -678,8 +678,8 @@ synth_event_trace_start() and synth_event_trace_end() along with
 synth_event_add_next_val() or synth_event_add_val() to add the values
 piecewise.
 
-6.3.3.1 Tracing a synthetic event all at once
----------------------------------------------
+7.2.1 Tracing a synthetic event all at once
+-------------------------------------------
 
 To trace a synthetic event all at once, the synth_event_trace() or
 synth_event_trace_array() functions can be used.
@@ -780,8 +780,8 @@ remove the event::
 
        ret = synth_event_delete("schedtest");
 
-6.3.3.1 Tracing a synthetic event piecewise
--------------------------------------------
+7.2.2 Tracing a synthetic event piecewise
+-----------------------------------------
 
 To trace a synthetic using the piecewise method described above, the
 synth_event_trace_start() function is used to 'open' the synthetic
@@ -864,8 +864,8 @@ Note that synth_event_trace_end() must be called at the end regardless
 of whether any of the add calls failed (say due to a bad field name
 being passed in).
 
-6.3.4 Dyamically creating kprobe and kretprobe event definitions
-----------------------------------------------------------------
+7.3 Dyamically creating kprobe and kretprobe event definitions
+--------------------------------------------------------------
 
 To create a kprobe or kretprobe trace event from kernel code, the
 kprobe_event_gen_cmd_start() or kretprobe_event_gen_cmd_start()
@@ -941,8 +941,8 @@ used to give the kprobe event file back and delete the event::
 
   ret = kprobe_event_delete("gen_kprobe_test");
 
-6.3.4 The "dynevent_cmd" low-level API
---------------------------------------
+7.4 The "dynevent_cmd" low-level API
+------------------------------------
 
 Both the in-kernel synthetic event and kprobe interfaces are built on
 top of a lower-level "dynevent_cmd" interface.  This interface is
index a8e22e0db63c28f2871673985b58a29b732236b7..6893399157f0b87792cea4cf16915bab6200a7d3 100644 (file)
@@ -229,14 +229,6 @@ Adding support for it is easy: just define the macro in asm/ftrace.h and
 pass the return address pointer as the 'retp' argument to
 ftrace_push_return_trace().
 
-HAVE_FTRACE_NMI_ENTER
----------------------
-
-If you can't trace NMI functions, then skip this option.
-
-<details to be filled>
-
-
 HAVE_SYSCALL_TRACEPOINTS
 ------------------------
 
index a4ecd8f27631a1608993864057e6d7effef16af2..524ad86cadbbfb2dcb2f3d8cdf00d2bfe1b26040 100644 (file)
@@ -515,6 +515,22 @@ internal: *[source-pattern ...]*
     .. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
        :internal:
 
+identifiers: *[ function/type ...]*
+  Include la documentazione per ogni *function* e *type*  in *source*.
+  Se non vengono esplicitamente specificate le funzioni da includere, allora
+  verranno incluse tutte quelle disponibili in *source*.
+
+  Esempi::
+
+    .. kernel-doc:: lib/bitmap.c
+       :identifiers: bitmap_parselist bitmap_parselist_user
+
+    .. kernel-doc:: lib/idr.c
+       :identifiers:
+
+functions: *[ function ...]*
+  Questo è uno pseudonimo, deprecato, per la direttiva 'identifiers'.
+
 doc: *title*
   Include la documentazione del paragrafo ``DOC:`` identificato dal titolo
   (*title*) all'interno del file sorgente (*source*). Gli spazi in *title* sono
@@ -528,15 +544,6 @@ doc: *title*
     .. kernel-doc:: drivers/gpu/drm/i915/intel_audio.c
        :doc: High Definition Audio over HDMI and Display Port
 
-functions: *function* *[...]*
-  Dal file sorgente (*source*) include la documentazione per le funzioni
-  elencate (*function*).
-
-  Esempio::
-
-    .. kernel-doc:: lib/bitmap.c
-       :functions: bitmap_parselist bitmap_parselist_user
-
 Senza alcuna opzione, la direttiva kernel-doc include tutti i commenti di
 documentazione presenti nel file sorgente (*source*).
 
index 24c592852bf1fd99a6e0d91a795cb5bf4b6d3050..6aab27a8d3238d8c52cefb190db9807b48726d9f 100644 (file)
@@ -627,6 +627,24 @@ Alcuni manutentori e sviluppatori potrebbero comunque richiedere
 :c:func:`EXPORT_SYMBOL_GPL()` quando si aggiungono nuove funzionalità o
 interfacce.
 
+:c:func:`EXPORT_SYMBOL_NS()`
+----------------------------
+
+Definita in ``include/linux/export.h``
+
+Questa è una variate di `EXPORT_SYMBOL()` che permette di specificare uno
+spazio dei nomi. Lo spazio dei nomi è documentato in
+:doc:`../core-api/symbol-namespaces`
+
+:c:func:`EXPORT_SYMBOL_NS_GPL()`
+--------------------------------
+
+Definita in ``include/linux/export.h``
+
+Questa è una variate di `EXPORT_SYMBOL_GPL()` che permette di specificare uno
+spazio dei nomi. Lo spazio dei nomi è documentato in
+:doc:`../core-api/symbol-namespaces`
+
 Procedure e convenzioni
 =======================
 
index b9a6be4b8499e76dcf7cd33632d6b3a403534a8d..4615df5723fb68283b24a97d74bde0306882092b 100644 (file)
@@ -159,17 +159,17 @@ Sincronizzazione in contesto utente
 Se avete una struttura dati che verrà utilizzata solo dal contesto utente,
 allora, per proteggerla, potete utilizzare un semplice mutex
 (``include/linux/mutex.h``). Questo è il caso più semplice: inizializzate il
-mutex; invocate :c:func:`mutex_lock_interruptible()` per trattenerlo e
-:c:func:`mutex_unlock()` per rilasciarlo. C'è anche :c:func:`mutex_lock()`
+mutex; invocate mutex_lock_interruptible() per trattenerlo e
+mutex_unlock() per rilasciarlo. C'è anche mutex_lock()
 ma questa dovrebbe essere evitata perché non ritorna in caso di segnali.
 
 Per esempio: ``net/netfilter/nf_sockopt.c`` permette la registrazione
-di nuove chiamate per :c:func:`setsockopt()` e :c:func:`getsockopt()`
-usando la funzione :c:func:`nf_register_sockopt()`. La registrazione e
+di nuove chiamate per setsockopt() e getsockopt()
+usando la funzione nf_register_sockopt(). La registrazione e
 la rimozione vengono eseguite solamente quando il modulo viene caricato
 o scaricato (e durante l'avvio del sistema, qui non abbiamo concorrenza),
 e la lista delle funzioni registrate viene consultata solamente quando
-:c:func:`setsockopt()` o :c:func:`getsockopt()` sono sconosciute al sistema.
+setsockopt() o getsockopt() sono sconosciute al sistema.
 In questo caso ``nf_sockopt_mutex`` è perfetto allo scopo, in particolar modo
 visto che setsockopt e getsockopt potrebbero dormire.
 
@@ -179,19 +179,19 @@ Sincronizzazione fra il contesto utente e i softirq
 Se un softirq condivide dati col contesto utente, avete due problemi.
 Primo, il contesto utente corrente potrebbe essere interroto da un softirq,
 e secondo, la sezione critica potrebbe essere eseguita da un altro
-processore. Questo è quando :c:func:`spin_lock_bh()`
+processore. Questo è quando spin_lock_bh()
 (``include/linux/spinlock.h``) viene utilizzato. Questo disabilita i softirq
-sul processore e trattiene il *lock*. Invece, :c:func:`spin_unlock_bh()` fa
+sul processore e trattiene il *lock*. Invece, spin_unlock_bh() fa
 l'opposto. (Il suffisso '_bh' è un residuo storico che fa riferimento al
 "Bottom Halves", il vecchio nome delle interruzioni software. In un mondo
 perfetto questa funzione si chiamerebbe 'spin_lock_softirq()').
 
-Da notare che in questo caso potete utilizzare anche :c:func:`spin_lock_irq()`
-o :c:func:`spin_lock_irqsave()`, queste fermano anche le interruzioni hardware:
+Da notare che in questo caso potete utilizzare anche spin_lock_irq()
+o spin_lock_irqsave(), queste fermano anche le interruzioni hardware:
 vedere :ref:`Contesto di interruzione hardware <it_hardirq-context>`.
 
 Questo funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
-svaniscono e questa macro diventa semplicemente :c:func:`local_bh_disable()`
+svaniscono e questa macro diventa semplicemente local_bh_disable()
 (``include/linux/interrupt.h``), la quale impedisce ai softirq d'essere
 eseguiti.
 
@@ -224,8 +224,8 @@ Differenti tasklet/timer
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
 Se un altro tasklet/timer vuole condividere dati col vostro tasklet o timer,
-allora avrete bisogno entrambe di :c:func:`spin_lock()` e
-:c:func:`spin_unlock()`. Qui :c:func:`spin_lock_bh()` è inutile, siete già
+allora avrete bisogno entrambe di spin_lock() e
+spin_unlock(). Qui spin_lock_bh() è inutile, siete già
 in un tasklet ed avete la garanzia che nessun altro verrà eseguito sullo
 stesso processore.
 
@@ -243,13 +243,13 @@ processore (vedere :ref:`Dati per processore <it_per-cpu>`). Se siete arrivati
 fino a questo punto nell'uso dei softirq, probabilmente tenete alla scalabilità
 delle prestazioni abbastanza da giustificarne la complessità aggiuntiva.
 
-Dovete utilizzare :c:func:`spin_lock()` e :c:func:`spin_unlock()` per
+Dovete utilizzare spin_lock() e spin_unlock() per
 proteggere i dati condivisi.
 
 Diversi Softirqs
 ~~~~~~~~~~~~~~~~
 
-Dovete utilizzare :c:func:`spin_lock()` e :c:func:`spin_unlock()` per
+Dovete utilizzare spin_lock() e spin_unlock() per
 proteggere i dati condivisi, che siano timer, tasklet, diversi softirq o
 lo stesso o altri softirq: uno qualsiasi di essi potrebbe essere in esecuzione
 su un diverso processore.
@@ -270,40 +270,40 @@ Se un gestore di interruzioni hardware condivide dati con un softirq, allora
 avrete due preoccupazioni. Primo, il softirq può essere interrotto da
 un'interruzione hardware, e secondo, la sezione critica potrebbe essere
 eseguita da un'interruzione hardware su un processore diverso. Questo è il caso
-dove :c:func:`spin_lock_irq()` viene utilizzato. Disabilita le interruzioni
-sul processore che l'esegue, poi trattiene il lock. :c:func:`spin_unlock_irq()`
+dove spin_lock_irq() viene utilizzato. Disabilita le interruzioni
+sul processore che l'esegue, poi trattiene il lock. spin_unlock_irq()
 fa l'opposto.
 
-Il gestore d'interruzione hardware non usa :c:func:`spin_lock_irq()` perché
-i softirq non possono essere eseguiti quando il gestore d'interruzione hardware
-è in esecuzione: per questo si può usare :c:func:`spin_lock()`, che è un po'
+Il gestore d'interruzione hardware non ha bisogno di usare spin_lock_irq()
+perché i softirq non possono essere eseguiti quando il gestore d'interruzione
+hardware è in esecuzione: per questo si può usare spin_lock(), che è un po'
 più veloce. L'unica eccezione è quando un altro gestore d'interruzioni
-hardware utilizza lo stesso *lock*: :c:func:`spin_lock_irq()` impedirà a questo
+hardware utilizza lo stesso *lock*: spin_lock_irq() impedirà a questo
 secondo gestore di interrompere quello in esecuzione.
 
 Questo funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
-svaniscono e questa macro diventa semplicemente :c:func:`local_irq_disable()`
+svaniscono e questa macro diventa semplicemente local_irq_disable()
 (``include/asm/smp.h``), la quale impedisce a softirq/tasklet/BH d'essere
 eseguiti.
 
-:c:func:`spin_lock_irqsave()` (``include/linux/spinlock.h``) è una variante che
+spin_lock_irqsave() (``include/linux/spinlock.h``) è una variante che
 salva lo stato delle interruzioni in una variabile, questa verrà poi passata
-a :c:func:`spin_unlock_irqrestore()`. Questo significa che lo stesso codice
+a spin_unlock_irqrestore(). Questo significa che lo stesso codice
 potrà essere utilizzato in un'interruzione hardware (dove le interruzioni sono
 già disabilitate) e in un softirq (dove la disabilitazione delle interruzioni
 è richiesta).
 
 Da notare che i softirq (e quindi tasklet e timer) sono eseguiti al ritorno
-da un'interruzione hardware, quindi :c:func:`spin_lock_irq()` interrompe
+da un'interruzione hardware, quindi spin_lock_irq() interrompe
 anche questi. Tenuto conto di questo si può dire che
-:c:func:`spin_lock_irqsave()` è la funzione di sincronizzazione più generica
+spin_lock_irqsave() è la funzione di sincronizzazione più generica
 e potente.
 
 Sincronizzazione fra due gestori d'interruzioni hardware
 --------------------------------------------------------
 
 Condividere dati fra due gestori di interruzione hardware è molto raro, ma se
-succede, dovreste usare :c:func:`spin_lock_irqsave()`: è una specificità
+succede, dovreste usare spin_lock_irqsave(): è una specificità
 dell'architettura il fatto che tutte le interruzioni vengano interrotte
 quando si eseguono di gestori di interruzioni.
 
@@ -317,11 +317,11 @@ Pete Zaitcev ci offre il seguente riassunto:
    il mutex e dormire (``copy_from_user*(`` o ``kmalloc(x,GFP_KERNEL)``).
 
 -  Altrimenti (== i dati possono essere manipolati da un'interruzione) usate
-   :c:func:`spin_lock_irqsave()` e :c:func:`spin_unlock_irqrestore()`.
+   spin_lock_irqsave() e spin_unlock_irqrestore().
 
 -  Evitate di trattenere uno spinlock per più di 5 righe di codice incluse
    le chiamate a funzione (ad eccezione di quell per l'accesso come
-   :c:func:`readb()`).
+   readb()).
 
 Tabella dei requisiti minimi
 ----------------------------
@@ -334,7 +334,7 @@ processore alla volta, ma se deve condividere dati con un altro thread, allora
 la sincronizzazione è necessaria).
 
 Ricordatevi il suggerimento qui sopra: potete sempre usare
-:c:func:`spin_lock_irqsave()`, che è un sovrainsieme di tutte le altre funzioni
+spin_lock_irqsave(), che è un sovrainsieme di tutte le altre funzioni
 per spinlock.
 
 ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
@@ -378,13 +378,13 @@ protetti dal *lock* quando qualche altro thread lo sta già facendo
 trattenendo il *lock*. Potrete acquisire il *lock* più tardi se vi
 serve accedere ai dati protetti da questo *lock*.
 
-La funzione :c:func:`spin_trylock()` non ritenta di acquisire il *lock*,
+La funzione spin_trylock() non ritenta di acquisire il *lock*,
 se ci riesce al primo colpo ritorna un valore diverso da zero, altrimenti
 se fallisce ritorna 0. Questa funzione può essere utilizzata in un qualunque
-contesto, ma come :c:func:`spin_lock()`: dovete disabilitare i contesti che
+contesto, ma come spin_lock(): dovete disabilitare i contesti che
 potrebbero interrompervi e quindi trattenere lo spinlock.
 
-La funzione :c:func:`mutex_trylock()` invece di sospendere il vostro processo
+La funzione mutex_trylock() invece di sospendere il vostro processo
 ritorna un valore diverso da zero se è possibile trattenere il lock al primo
 colpo, altrimenti se fallisce ritorna 0. Nonostante non dorma, questa funzione
 non può essere usata in modo sicuro in contesti di interruzione hardware o
@@ -506,7 +506,7 @@ della memoria che il suo contenuto sono protetti dal *lock*. Questo
 caso è semplice dato che copiamo i dati dall'utente e non permettiamo
 mai loro di accedere direttamente agli oggetti.
 
-C'è una piccola ottimizzazione qui: nella funzione :c:func:`cache_add()`
+C'è una piccola ottimizzazione qui: nella funzione cache_add()
 impostiamo i campi dell'oggetto prima di acquisire il *lock*. Questo è
 sicuro perché nessun altro potrà accedervi finché non lo inseriremo
 nella memoria.
@@ -514,7 +514,7 @@ nella memoria.
 Accesso dal contesto utente
 ---------------------------
 
-Ora consideriamo il caso in cui :c:func:`cache_find()` può essere invocata
+Ora consideriamo il caso in cui cache_find() può essere invocata
 dal contesto d'interruzione: sia hardware che software. Un esempio potrebbe
 essere un timer che elimina oggetti dalla memoria.
 
@@ -583,15 +583,15 @@ sono quelle rimosse, mentre quelle ``+`` sono quelle aggiunte.
              return ret;
      }
 
-Da notare che :c:func:`spin_lock_irqsave()` disabiliterà le interruzioni
+Da notare che spin_lock_irqsave() disabiliterà le interruzioni
 se erano attive, altrimenti non farà niente (quando siamo già in un contesto
 d'interruzione); dunque queste funzioni possono essere chiamante in
 sicurezza da qualsiasi contesto.
 
-Sfortunatamente, :c:func:`cache_add()` invoca :c:func:`kmalloc()` con
+Sfortunatamente, cache_add() invoca kmalloc() con
 l'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto
-che :c:func:`cache_add()` venga chiamata dal contesto utente, altrimenti
-questa opzione deve diventare un parametro di :c:func:`cache_add()`.
+che cache_add() venga chiamata dal contesto utente, altrimenti
+questa opzione deve diventare un parametro di cache_add().
 
 Esporre gli oggetti al di fuori del file
 ----------------------------------------
@@ -610,7 +610,7 @@ Il secondo problema è il problema del ciclo di vita: se un'altra struttura
 mantiene un puntatore ad un oggetto, presumibilmente si aspetta che questo
 puntatore rimanga valido. Sfortunatamente, questo è garantito solo mentre
 si trattiene il *lock*, altrimenti qualcuno potrebbe chiamare
-:c:func:`cache_delete()` o peggio, aggiungere un oggetto che riutilizza lo
+cache_delete() o peggio, aggiungere un oggetto che riutilizza lo
 stesso indirizzo.
 
 Dato che c'è un solo *lock*, non potete trattenerlo a vita: altrimenti
@@ -710,9 +710,9 @@ Ecco il codice::
      }
 
 Abbiamo incapsulato il contatore di riferimenti nelle tipiche funzioni
-di 'get' e 'put'. Ora possiamo ritornare l'oggetto da :c:func:`cache_find()`
+di 'get' e 'put'. Ora possiamo ritornare l'oggetto da cache_find()
 col vantaggio che l'utente può dormire trattenendo l'oggetto (per esempio,
-:c:func:`copy_to_user()` per copiare il nome verso lo spazio utente).
+copy_to_user() per copiare il nome verso lo spazio utente).
 
 Un altro punto da notare è che ho detto che il contatore dovrebbe incrementarsi
 per ogni puntatore ad un oggetto: quindi il contatore di riferimenti è 1
@@ -727,8 +727,8 @@ Ci sono un certo numbero di operazioni atomiche definite
 in ``include/asm/atomic.h``: queste sono garantite come atomiche su qualsiasi
 processore del sistema, quindi non sono necessari i *lock*. In questo caso è
 più semplice rispetto all'uso degli spinlock, benché l'uso degli spinlock
-sia più elegante per casi non banali. Le funzioni :c:func:`atomic_inc()` e
-:c:func:`atomic_dec_and_test()` vengono usate al posto dei tipici operatori di
+sia più elegante per casi non banali. Le funzioni atomic_inc() e
+atomic_dec_and_test() vengono usate al posto dei tipici operatori di
 incremento e decremento, e i *lock* non sono più necessari per proteggere il
 contatore stesso.
 
@@ -820,7 +820,7 @@ al nome di cambiare abbiamo tre possibilità:
 -  Si può togliere static da ``cache_lock`` e dire agli utenti che devono
    trattenere il *lock* prima di modificare il nome di un oggetto.
 
--  Si può fornire una funzione :c:func:`cache_obj_rename()` che prende il
+-  Si può fornire una funzione cache_obj_rename() che prende il
    *lock* e cambia il nome per conto del chiamante; si dirà poi agli utenti
    di usare questa funzione.
 
@@ -878,11 +878,11 @@ Da notare che ho deciso che il contatore di popolarità dovesse essere
 protetto da ``cache_lock`` piuttosto che dal *lock* dell'oggetto; questo
 perché è logicamente parte dell'infrastruttura (come
 :c:type:`struct list_head <list_head>` nell'oggetto). In questo modo,
-in :c:func:`__cache_add()`, non ho bisogno di trattenere il *lock* di ogni
+in __cache_add(), non ho bisogno di trattenere il *lock* di ogni
 oggetto mentre si cerca il meno popolare.
 
 Ho anche deciso che il campo id è immutabile, quindi non ho bisogno di
-trattenere il lock dell'oggetto quando si usa :c:func:`__cache_find()`
+trattenere il lock dell'oggetto quando si usa __cache_find()
 per leggere questo campo; il *lock* dell'oggetto è usato solo dal chiamante
 che vuole leggere o scrivere il campo name.
 
@@ -907,7 +907,7 @@ Questo è facile da diagnosticare: non è uno di quei problemi che ti tengono
 sveglio 5 notti a parlare da solo.
 
 Un caso un pochino più complesso; immaginate d'avere una spazio condiviso
-fra un softirq ed il contesto utente. Se usate :c:func:`spin_lock()` per
+fra un softirq ed il contesto utente. Se usate spin_lock() per
 proteggerlo, il contesto utente potrebbe essere interrotto da un softirq
 mentre trattiene il lock, da qui il softirq rimarrà in attesa attiva provando
 ad acquisire il *lock* già trattenuto nel contesto utente.
@@ -1006,12 +1006,12 @@ potreste fare come segue::
             spin_unlock_bh(&list_lock);
 
 Primo o poi, questo esploderà su un sistema multiprocessore perché un
-temporizzatore potrebbe essere già partiro prima di :c:func:`spin_lock_bh()`,
-e prenderà il *lock* solo dopo :c:func:`spin_unlock_bh()`, e cercherà
+temporizzatore potrebbe essere già partiro prima di spin_lock_bh(),
+e prenderà il *lock* solo dopo spin_unlock_bh(), e cercherà
 di eliminare il suo oggetto (che però è già stato eliminato).
 
 Questo può essere evitato controllando il valore di ritorno di
-:c:func:`del_timer()`: se ritorna 1, il temporizzatore è stato già
+del_timer(): se ritorna 1, il temporizzatore è stato già
 rimosso. Se 0, significa (in questo caso) che il temporizzatore è in
 esecuzione, quindi possiamo fare come segue::
 
@@ -1032,9 +1032,9 @@ esecuzione, quindi possiamo fare come segue::
                     spin_unlock_bh(&list_lock);
 
 Un altro problema è l'eliminazione dei temporizzatori che si riavviano
-da soli (chiamando :c:func:`add_timer()` alla fine della loro esecuzione).
+da soli (chiamando add_timer() alla fine della loro esecuzione).
 Dato che questo è un problema abbastanza comune con una propensione
-alle corse critiche, dovreste usare :c:func:`del_timer_sync()`
+alle corse critiche, dovreste usare del_timer_sync()
 (``include/linux/timer.h``) per gestire questo caso. Questa ritorna il
 numero di volte che il temporizzatore è stato interrotto prima che
 fosse in grado di fermarlo senza che si riavviasse.
@@ -1116,7 +1116,7 @@ chiamata ``list``::
             wmb();
             list->next = new;
 
-La funzione :c:func:`wmb()` è una barriera di sincronizzazione delle
+La funzione wmb() è una barriera di sincronizzazione delle
 scritture. Questa garantisce che la prima operazione (impostare l'elemento
 ``next`` del nuovo elemento) venga completata e vista da tutti i processori
 prima che venga eseguita la seconda operazione (che sarebbe quella di mettere
@@ -1127,7 +1127,7 @@ completamente il nuovo elemento; oppure che lo vedano correttamente e quindi
 il puntatore ``next`` deve puntare al resto della lista.
 
 Fortunatamente, c'è una funzione che fa questa operazione sulle liste
-:c:type:`struct list_head <list_head>`: :c:func:`list_add_rcu()`
+:c:type:`struct list_head <list_head>`: list_add_rcu()
 (``include/linux/list.h``).
 
 Rimuovere un elemento dalla lista è anche più facile: sostituiamo il puntatore
@@ -1138,7 +1138,7 @@ l'elemento o lo salteranno.
 
             list->next = old->next;
 
-La funzione :c:func:`list_del_rcu()` (``include/linux/list.h``) fa esattamente
+La funzione list_del_rcu() (``include/linux/list.h``) fa esattamente
 questo (la versione normale corrompe il vecchio oggetto, e non vogliamo che
 accada).
 
@@ -1146,9 +1146,9 @@ Anche i lettori devono stare attenti: alcuni processori potrebbero leggere
 attraverso il puntatore ``next`` il contenuto dell'elemento successivo
 troppo presto, ma non accorgersi che il contenuto caricato è sbagliato quando
 il puntatore ``next`` viene modificato alla loro spalle. Ancora una volta
-c'è una funzione che viene in vostro aiuto :c:func:`list_for_each_entry_rcu()`
+c'è una funzione che viene in vostro aiuto list_for_each_entry_rcu()
 (``include/linux/list.h``). Ovviamente, gli scrittori possono usare
-:c:func:`list_for_each_entry()` dato che non ci possono essere due scrittori
+list_for_each_entry() dato che non ci possono essere due scrittori
 in contemporanea.
 
 Il nostro ultimo dilemma è il seguente: quando possiamo realmente distruggere
@@ -1156,15 +1156,15 @@ l'elemento rimosso? Ricordate, un lettore potrebbe aver avuto accesso a questo
 elemento proprio ora: se eliminiamo questo elemento ed il puntatore ``next``
 cambia, il lettore salterà direttamente nella spazzatura e scoppierà. Dobbiamo
 aspettare finché tutti i lettori che stanno attraversando la lista abbiano
-finito. Utilizziamo :c:func:`call_rcu()` per registrare una funzione di
+finito. Utilizziamo call_rcu() per registrare una funzione di
 richiamo che distrugga l'oggetto quando tutti i lettori correnti hanno
 terminato. In alternative, potrebbe essere usata la funzione
-:c:func:`synchronize_rcu()` che blocca l'esecuzione finché tutti i lettori
+synchronize_rcu() che blocca l'esecuzione finché tutti i lettori
 non terminano di ispezionare la lista.
 
 Ma come fa l'RCU a sapere quando i lettori sono finiti? Il meccanismo è
 il seguente: innanzi tutto i lettori accedono alla lista solo fra la coppia
-:c:func:`rcu_read_lock()`/:c:func:`rcu_read_unlock()` che disabilita la
+rcu_read_lock()/rcu_read_unlock() che disabilita la
 prelazione così che i lettori non vengano sospesi mentre stanno leggendo
 la lista.
 
@@ -1253,12 +1253,12 @@ codice RCU è un po' più ottimizzato di così, ma questa è l'idea di fondo.
      }
 
 Da notare che i lettori modificano il campo popularity nella funzione
-:c:func:`__cache_find()`, e ora non trattiene alcun *lock*. Una soluzione
+__cache_find(), e ora non trattiene alcun *lock*. Una soluzione
 potrebbe essere quella di rendere la variabile ``atomic_t``, ma per l'uso
 che ne abbiamo fatto qui, non ci interessano queste corse critiche perché un
 risultato approssimativo è comunque accettabile, quindi non l'ho cambiato.
 
-Il risultato è che la funzione :c:func:`cache_find()` non ha bisogno di alcuna
+Il risultato è che la funzione cache_find() non ha bisogno di alcuna
 sincronizzazione con le altre funzioni, quindi è veloce su un sistema
 multi-processore tanto quanto lo sarebbe su un sistema mono-processore.
 
@@ -1271,9 +1271,9 @@ riferimenti.
 
 Ora, dato che il '*lock* di lettura' di un RCU non fa altro che disabilitare
 la prelazione, un chiamante che ha sempre la prelazione disabilitata fra le
-chiamate :c:func:`cache_find()` e :c:func:`object_put()` non necessita
+chiamate cache_find() e object_put() non necessita
 di incrementare e decrementare il contatore di riferimenti. Potremmo
-esporre la funzione :c:func:`__cache_find()` dichiarandola non-static,
+esporre la funzione __cache_find() dichiarandola non-static,
 e quel chiamante potrebbe usare direttamente questa funzione.
 
 Il beneficio qui sta nel fatto che il contatore di riferimenti no
@@ -1293,10 +1293,10 @@ singolo contatore. Facile e pulito.
 Se questo dovesse essere troppo lento (solitamente non lo è, ma se avete
 dimostrato che lo è devvero), potreste usare un contatore per ogni processore
 e quindi non sarebbe più necessaria la mutua esclusione. Vedere
-:c:func:`DEFINE_PER_CPU()`, :c:func:`get_cpu_var()` e :c:func:`put_cpu_var()`
+DEFINE_PER_CPU(), get_cpu_var() e put_cpu_var()
 (``include/linux/percpu.h``).
 
-Il tipo di dato ``local_t``, la funzione :c:func:`cpu_local_inc()` e tutte
+Il tipo di dato ``local_t``, la funzione cpu_local_inc() e tutte
 le altre funzioni associate, sono di particolare utilità per semplici contatori
 per-processore; su alcune architetture sono anche più efficienti
 (``include/asm/local.h``).
@@ -1324,11 +1324,11 @@ da un'interruzione software. Il gestore d'interruzione non utilizza alcun
         enable_irq(irq);
         spin_unlock(&lock);
 
-La funzione :c:func:`disable_irq()` impedisce al gestore d'interruzioni
+La funzione disable_irq() impedisce al gestore d'interruzioni
 d'essere eseguito (e aspetta che finisca nel caso fosse in esecuzione su
 un altro processore). Lo spinlock, invece, previene accessi simultanei.
 Naturalmente, questo è più lento della semplice chiamata
-:c:func:`spin_lock_irq()`, quindi ha senso solo se questo genere di accesso
+spin_lock_irq(), quindi ha senso solo se questo genere di accesso
 è estremamente raro.
 
 .. _`it_sleeping-things`:
@@ -1336,7 +1336,7 @@ Naturalmente, questo è più lento della semplice chiamata
 Quali funzioni possono essere chiamate in modo sicuro dalle interruzioni?
 =========================================================================
 
-Molte funzioni del kernel dormono (in sostanza, chiamano ``schedule()``)
+Molte funzioni del kernel dormono (in sostanza, chiamano schedule())
 direttamente od indirettamente: non potete chiamarle se trattenere uno
 spinlock o avete la prelazione disabilitata, mai. Questo significa che
 dovete necessariamente essere nel contesto utente: chiamarle da un
@@ -1354,23 +1354,23 @@ dormire.
 
 -  Accessi allo spazio utente:
 
-   -  :c:func:`copy_from_user()`
+   -  copy_from_user()
 
-   -  :c:func:`copy_to_user()`
+   -  copy_to_user()
 
-   -  :c:func:`get_user()`
+   -  get_user()
 
-   -  :c:func:`put_user()`
+   -  put_user()
 
--  :c:func:`kmalloc(GFP_KERNEL) <kmalloc>`
+-  kmalloc(GFP_KERNEL) <kmalloc>`
 
--  :c:func:`mutex_lock_interruptible()` and
-   :c:func:`mutex_lock()`
+-  mutex_lock_interruptible() and
+   mutex_lock()
 
-   C'è anche :c:func:`mutex_trylock()` che però non dorme.
+   C'è anche mutex_trylock() che però non dorme.
    Comunque, non deve essere usata in un contesto d'interruzione dato
    che la sua implementazione non è sicura in quel contesto.
-   Anche :c:func:`mutex_unlock()` non dorme mai. Non può comunque essere
+   Anche mutex_unlock() non dorme mai. Non può comunque essere
    usata in un contesto d'interruzione perché un mutex deve essere rilasciato
    dallo stesso processo che l'ha acquisito.
 
@@ -1380,11 +1380,11 @@ Alcune funzioni che non dormono
 Alcune funzioni possono essere chiamate tranquillamente da qualsiasi
 contesto, o trattenendo un qualsiasi *lock*.
 
--  :c:func:`printk()`
+-  printk()
 
--  :c:func:`kfree()`
+-  kfree()
 
--  :c:func:`add_timer()` e :c:func:`del_timer()`
+-  add_timer() e del_timer()
 
 Riferimento per l'API dei Mutex
 ===============================
@@ -1444,14 +1444,14 @@ prelazione
 bh
   Bottom Half: per ragioni storiche, le funzioni che contengono '_bh' nel
   loro nome ora si riferiscono a qualsiasi interruzione software; per esempio,
-  :c:func:`spin_lock_bh()` blocca qualsiasi interuzione software sul processore
+  spin_lock_bh() blocca qualsiasi interuzione software sul processore
   corrente. I *Bottom Halves* sono deprecati, e probabilmente verranno
   sostituiti dai tasklet. In un dato momento potrà esserci solo un
   *bottom half* in esecuzione.
 
 contesto d'interruzione
   Non è il contesto utente: qui si processano le interruzioni hardware e
-  software. La macro :c:func:`in_interrupt()` ritorna vero.
+  software. La macro in_interrupt() ritorna vero.
 
 contesto utente
   Il kernel che esegue qualcosa per conto di un particolare processo (per
@@ -1461,12 +1461,12 @@ contesto utente
   che hardware.
 
 interruzione hardware
-  Richiesta di interruzione hardware. :c:func:`in_irq()` ritorna vero in un
+  Richiesta di interruzione hardware. in_irq() ritorna vero in un
   gestore d'interruzioni hardware.
 
 interruzione software / softirq
-  Gestore di interruzioni software: :c:func:`in_irq()` ritorna falso;
-  :c:func:`in_softirq()` ritorna vero. I tasklet e le softirq sono entrambi
+  Gestore di interruzioni software: in_irq() ritorna falso;
+  in_softirq() ritorna vero. I tasklet e le softirq sono entrambi
   considerati 'interruzioni software'.
 
   In soldoni, un softirq è uno delle 32 interruzioni software che possono
index 9af4d01617c49a79bc38aaddb9379b60ab973c31..30dc172f06b036d03be21e09f4b0187265c2f570 100644 (file)
@@ -23,18 +23,18 @@ ogni due o tre mesi viene effettuata un rilascio importante del kernel.
 I rilasci più recenti sono stati:
 
        ======  =================
-       4.11    Aprile 30, 2017
-       4.12    Luglio 2, 2017
-       4.13    Settembre 3, 2017
-       4.14    Novembre 12, 2017
-       4.15    Gennaio 28, 2018
-       4.16    Aprile 1, 2018
+       5.0     3 marzo, 2019
+       5.1     5 maggio, 2019
+       5.2     7 luglio, 2019
+       5.3     15 settembre, 2019
+       5.4     24 novembre, 2019
+       5.5     6 gennaio, 2020
        ======  =================
 
-Ciascun rilascio 4.x è un importante rilascio del kernel con nuove
+Ciascun rilascio 5.x è un importante rilascio del kernel con nuove
 funzionalità, modifiche interne dell'API, e molto altro.  Un tipico
-rilascio 4.x contiene quasi 13,000 gruppi di modifiche con ulteriori
-modifiche a parecchie migliaia di linee di codice.  La 4.x. è pertanto la
+rilascio contiene quasi 13,000 gruppi di modifiche con ulteriori
+modifiche a parecchie migliaia di linee di codice.  La 5.x. è pertanto la
 linea di confine nello sviluppo del kernel Linux; il kernel utilizza un sistema
 di sviluppo continuo che integra costantemente nuove importanti modifiche.
 
@@ -55,8 +55,8 @@ verrà descritto dettagliatamente più avanti).
 La finestra di inclusione resta attiva approssimativamente per due settimane.
 Al termine di questo periodo, Linus Torvald dichiarerà che la finestra è
 chiusa e rilascerà il primo degli "rc" del kernel.
-Per il kernel che è destinato ad essere 2.6.40, per esempio, il rilascio
-che emerge al termine della finestra d'inclusione si chiamerà 2.6.40-rc1.
+Per il kernel che è destinato ad essere 5.6, per esempio, il rilascio
+che emerge al termine della finestra d'inclusione si chiamerà 5.6-rc1.
 Questo rilascio indica che il momento di aggiungere nuovi componenti è
 passato, e che è iniziato il periodo di stabilizzazione del prossimo kernel.
 
@@ -76,22 +76,23 @@ Mentre le correzioni si aprono la loro strada all'interno del ramo principale,
 il ritmo delle modifiche rallenta col tempo.  Linus rilascia un nuovo
 kernel -rc circa una volta alla settimana; e ne usciranno circa 6 o 9 prima
 che il kernel venga considerato sufficientemente stabile e che il rilascio
-finale 2.6.x venga fatto.  A quel punto tutto il processo ricomincerà.
+finale venga fatto.  A quel punto tutto il processo ricomincerà.
 
-Esempio: ecco com'è andato il ciclo di sviluppo della versione 4.16
+Esempio: ecco com'è andato il ciclo di sviluppo della versione 5.4
 (tutte le date si collocano nel 2018)
 
 
        ==============  =======================================
-       Gennaio 28      4.15 rilascio stabile
-       Febbraio 11     4.16-rc1, finestra di inclusione chiusa
-       Febbraio 18     4.16-rc2
-       Febbraio 25     4.16-rc3
-       Marzo 4         4.16-rc4
-       Marzo 11        4.16-rc5
-       Marzo 18        4.16-rc6
-       Marzo 25        4.16-rc7
-       Aprile 1                4.17 rilascio stabile
+       15 settembre    5.3 rilascio stabile
+       30 settembre    5.4-rc1, finestra di inclusione chiusa
+       6 ottobre       5.4-rc2
+       13 ottobre      5.4-rc3
+       20 ottobre      5.4-rc4
+       27 ottobre      5.4-rc5
+       3 novembre      5.4-rc6
+       10 novembre     5.4-rc7
+       17 novembre     5.4-rc8
+       24 novembre     5.4 rilascio stabile
        ==============  =======================================
 
 In che modo gli sviluppatori decidono quando chiudere il ciclo di sviluppo e
@@ -108,43 +109,44 @@ tipo di perfezione difficilmente viene raggiunta; esistono troppe variabili
 in un progetto di questa portata.  Arriva un punto dove ritardare il rilascio
 finale peggiora la situazione; la quantità di modifiche in attesa della
 prossima finestra di inclusione crescerà enormemente, creando ancor più
-regressioni al giro successivo.  Quindi molti kernel 4.x escono con una
+regressioni al giro successivo.  Quindi molti kernel 5.x escono con una
 manciata di regressioni delle quali, si spera, nessuna è grave.
 
 Una volta che un rilascio stabile è fatto, il suo costante mantenimento è
 affidato al "squadra stabilità", attualmente composta da Greg Kroah-Hartman.
 Questa squadra rilascia occasionalmente degli aggiornamenti relativi al
-rilascio stabile usando la numerazione 4.x.y.  Per essere presa in
+rilascio stabile usando la numerazione 5.x.y.  Per essere presa in
 considerazione per un rilascio d'aggiornamento, una modifica deve:
 (1) correggere un baco importante (2) essere già inserita nel ramo principale
 per il prossimo sviluppo del kernel.  Solitamente, passato il loro rilascio
 iniziale, i kernel ricevono aggiornamenti per più di un ciclo di sviluppo.
-Quindi, per esempio, la storia del kernel 4.13 appare così:
+Quindi, per esempio, la storia del kernel 5.2 appare così (anno 2019):
 
        ==============  ===============================
-       Settembre 3     4.13 rilascio stabile
-       Settembre 13    4.13.1
-       Settembre 20    4.13.2
-       Settembre 27    4.13.3
-       Ottobre 5       4.13.4
-       Ottobre 12      4.13.5
+       15 settembre    5.2 rilascio stabile FIXME settembre è sbagliato
+       14 luglio       5.2.1
+       21 luglio       5.2.2
+       26 luglio       5.2.3
+       28 luglio       5.2.4
+       31 luglio       5.2.5
        ...             ...
-       Novembre 24     4.13.16
+       11 ottobre      5.2.21
        ==============  ===============================
 
-La 4.13.16 fu l'aggiornamento finale per la versione 4.13.
+La 5.2.21 fu l'aggiornamento finale per la versione 5.2.
 
 Alcuni kernel sono destinati ad essere kernel a "lungo termine"; questi
 riceveranno assistenza per un lungo periodo di tempo.  Al momento in cui
 scriviamo, i manutentori dei kernel stabili a lungo termine sono:
 
-       ======  ======================  ==========================================
-       3.16    Ben Hutchings           (kernel stabile molto più a lungo termine)
-       4.1     Sasha Levin
-       4.4     Greg Kroah-Hartman      (kernel stabile molto più a lungo termine)
-       4.9     Greg Kroah-Hartman
-       4.14    Greg Kroah-Hartman
-       ======  ======================  ==========================================
+       ======  ================================  ==========================================
+       3.16    Ben Hutchings                     (kernel stabile molto più a lungo termine)
+       4.4     Greg Kroah-Hartman e Sasha Levin  (kernel stabile molto più a lungo termine)
+       4.9     Greg Kroah-Hartman e Sasha Levin
+       4.14    Greg Kroah-Hartman e Sasha Levin
+       4.19    Greg Kroah-Hartman e Sasha Levin
+       5.4i    Greg Kroah-Hartman e Sasha Levin
+       ======  ================================  ==========================================
 
 
 Questa selezione di kernel di lungo periodo sono puramente dovuti ai loro
@@ -229,12 +231,13 @@ Come le modifiche finiscono nel Kernel
 --------------------------------------
 
 Esiste una sola persona che può inserire le patch nel repositorio principale
-del kernel: Linus Torvalds.  Ma, di tutte le 9500 patch che entrarono nella
-versione 2.6.38 del kernel, solo 112 (circa l'1,3%) furono scelte direttamente
-da Linus in persona.  Il progetto del kernel è cresciuto fino a raggiungere
-una dimensione tale per cui un singolo sviluppatore non può controllare e
-selezionare indipendentemente ogni modifica senza essere supportato.
-La via scelta dagli sviluppatori per indirizzare tale crescita è stata quella
+del kernel: Linus Torvalds.  Ma, per esempio, di tutte le 9500 patch
+che entrarono nella versione 2.6.38 del kernel, solo 112 (circa
+l'1,3%) furono scelte direttamente da Linus in persona.  Il progetto
+del kernel è cresciuto fino a raggiungere una dimensione tale per cui
+un singolo sviluppatore non può controllare e selezionare
+indipendentemente ogni modifica senza essere supportato.  La via
+scelta dagli sviluppatori per indirizzare tale crescita è stata quella
 di utilizzare un sistema di "sottotenenti" basato sulla fiducia.
 
 Il codice base del kernel è spezzato in una serie si sottosistemi: rete,
index c3a3439595a63c684be2f4c142b274ad728528fd..bff0a82bf127c4e27fe87103ad4c354f1ba784eb 100644 (file)
@@ -39,7 +39,7 @@ vostra interfaccia.
        un qualche modo opaca.
 
  - Se dovete esporre solo delle informazioni sul sistema, un nuovo nodo in
-   sysfs (vedere ``Documentation/filesystems/sysfs.txt``) o
+   sysfs (vedere ``Documentation/filesystems/sysfs.rst``) o
    in procfs potrebbe essere sufficiente.  Tuttavia, l'accesso a questi
    meccanismi richiede che il filesystem sia montato, il che potrebbe non
    essere sempre vero (per esempio, in ambienti come namespace/sandbox/chroot).
index 8725f2b9e96032d37ac6a3c379ce254fd652e5d8..6f4f85832deeaa1d95cac2ac44985c15ecd70089 100644 (file)
@@ -313,7 +313,7 @@ che conta gli utenti attivi, dovreste chiamarla ``count_active_users()`` o
 qualcosa di simile, **non** dovreste chiamarla ``cntusr()``.
 
 Codificare il tipo di funzione nel suo nome (quella cosa chiamata notazione
-ungherese) fa male al cervello - il compilatore conosce comunque il tipo e
+ungherese) è stupido - il compilatore conosce comunque il tipo e
 può verificarli, e inoltre confonde i programmatori.  Non c'è da
 sorprendersi che MicroSoft faccia programmi bacati.
 
@@ -825,8 +825,8 @@ linguaggio assembler.
 
 Agli sviluppatori del kernel piace essere visti come dotti. Tenete un occhio
 di riguardo per l'ortografia e farete una belle figura. In inglese, evitate
-l'uso di parole mozzate come ``dont``: usate ``do not`` oppure ``don't``.
-Scrivete messaggi concisi, chiari, e inequivocabili.
+l'uso incorretto di abbreviazioni come ``dont``: usate ``do not`` oppure
+``don't``.  Scrivete messaggi concisi, chiari, e inequivocabili.
 
 I messaggi del kernel non devono terminare con un punto fermo.
 
index 776f26732a94b7ef103e64e10d3fe43b1e5bb9a2..e108eaf82cf6719ddc02817c6bb83c7907fd54bd 100644 (file)
@@ -34,6 +34,33 @@ interfaccia come 'vecchia', questa non è una soluzione completa. L'interfaccia
 deve essere rimossa dal kernel, o aggiunta a questo documento per scoraggiarne
 l'uso.
 
+BUG() e BUG_ON()
+----------------
+Al loro posto usate WARN() e WARN_ON() per gestire le
+condizioni "impossibili" e gestitele come se fosse possibile farlo.
+Nonostante le funzioni della famiglia BUG() siano state progettate
+per asserire "situazioni impossibili" e interrompere in sicurezza un
+thread del kernel, queste si sono rivelate essere troppo rischiose
+(per esempio, in quale ordine rilasciare i *lock*? Ci sono stati che
+sono stati ripristinati?). Molto spesso l'uso di BUG()
+destabilizza il sistema o lo corrompe del tutto, il che rende
+impossibile un'attività di debug o anche solo leggere un rapporto
+circa l'errore.  Linus ha un'opinione molto critica al riguardo:
+`email 1
+<https://lore.kernel.org/lkml/CA+55aFy6jNLsywVYdGp83AMrXBo_P-pkjkphPGrO=82SPKCpLQ@mail.gmail.com/>`_,
+`email 2
+<https://lore.kernel.org/lkml/CAHk-=whDHsbK3HTOpTF=ue_o04onRwTEaK_ZoJp_fjbqq4+=Jw@mail.gmail.com/>`_
+
+Tenete presente che la famiglia di funzioni WARN() dovrebbe essere
+usato solo per situazioni che si suppone siano "impossibili".  Se
+volete avvisare gli utenti riguardo a qualcosa di possibile anche se
+indesiderato, usare le funzioni della famiglia pr_warn().  Chi
+amministra il sistema potrebbe aver attivato l'opzione sysctl
+*panic_on_warn* per essere sicuri che il sistema smetta di funzionare
+in caso si verifichino delle condizioni "inaspettate". (per esempio,
+date un'occhiata al questo `commit
+<https://git.kernel.org/linus/d4689846881d160a4d12a514e991a740bcb5d65a>`_)
+
 Calcoli codificati negli argomenti di un allocatore
 ----------------------------------------------------
 Il calcolo dinamico delle dimensioni (specialmente le moltiplicazioni) non
@@ -68,52 +95,81 @@ Invece, usate la seguente funzione::
 
        header = kzalloc(struct_size(header, item, count), GFP_KERNEL);
 
-Per maggiori dettagli fate riferimento a :c:func:`array_size`,
-:c:func:`array3_size`, e :c:func:`struct_size`, così come la famiglia di
-funzioni :c:func:`check_add_overflow` e :c:func:`check_mul_overflow`.
+Per maggiori dettagli fate riferimento a array_size(),
+array3_size(), e struct_size(), così come la famiglia di
+funzioni check_add_overflow() e check_mul_overflow().
 
 simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull()
 ----------------------------------------------------------------------
-Le funzioni :c:func:`simple_strtol`, :c:func:`simple_strtoll`,
-:c:func:`simple_strtoul`, e :c:func:`simple_strtoull` ignorano volutamente
+Le funzioni simple_strtol(), simple_strtoll(),
+simple_strtoul(), e simple_strtoull() ignorano volutamente
 i possibili overflow, e questo può portare il chiamante a generare risultati
-inaspettati. Le rispettive funzioni :c:func:`kstrtol`, :c:func:`kstrtoll`,
-:c:func:`kstrtoul`, e :c:func:`kstrtoull` sono da considerarsi le corrette
+inaspettati. Le rispettive funzioni kstrtol(), kstrtoll(),
+kstrtoul(), e kstrtoull() sono da considerarsi le corrette
 sostitute; tuttavia va notato che queste richiedono che la stringa sia
 terminata con il carattere NUL o quello di nuova riga.
 
 strcpy()
 --------
-La funzione :c:func:`strcpy` non fa controlli agli estremi del buffer
+La funzione strcpy() non fa controlli agli estremi del buffer
 di destinazione. Questo può portare ad un overflow oltre i limiti del
 buffer e generare svariati tipi di malfunzionamenti. Nonostante l'opzione
 `CONFIG_FORTIFY_SOURCE=y` e svariate opzioni del compilatore aiutano
 a ridurne il rischio, non c'è alcuna buona ragione per continuare ad usare
-questa funzione. La versione sicura da usare è :c:func:`strscpy`.
+questa funzione. La versione sicura da usare è strscpy().
 
 strncpy() su stringe terminate con NUL
 --------------------------------------
-L'utilizzo di :c:func:`strncpy` non fornisce alcuna garanzia sul fatto che
+L'utilizzo di strncpy() non fornisce alcuna garanzia sul fatto che
 il buffer di destinazione verrà terminato con il carattere NUL. Questo
 potrebbe portare a diversi overflow di lettura o altri malfunzionamenti
 causati, appunto, dalla mancanza del terminatore. Questa estende la
 terminazione nel buffer di destinazione quando la stringa d'origine è più
 corta; questo potrebbe portare ad una penalizzazione delle prestazioni per
 chi usa solo stringe terminate. La versione sicura da usare è
-:c:func:`strscpy`. (chi usa :c:func:`strscpy` e necessita di estendere la
-terminazione con NUL deve aggiungere una chiamata a :c:func:`memset`)
+strscpy(). (chi usa strscpy() e necessita di estendere la
+terminazione con NUL deve aggiungere una chiamata a memset())
 
-Se il chiamate no usa stringhe terminate con NUL, allore :c:func:`strncpy()`
+Se il chiamate no usa stringhe terminate con NUL, allore strncpy()()
 può continuare ad essere usata, ma i buffer di destinazione devono essere
 marchiati con l'attributo `__nonstring <https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html>`_
 per evitare avvisi durante la compilazione.
 
 strlcpy()
 ---------
-La funzione :c:func:`strlcpy`, per prima cosa, legge interamente il buffer di
+La funzione strlcpy(), per prima cosa, legge interamente il buffer di
 origine, magari leggendo più di quanto verrà effettivamente copiato. Questo
 è inefficiente e può portare a overflow di lettura quando la stringa non è
-terminata con NUL. La versione sicura da usare è :c:func:`strscpy`.
+terminata con NUL. La versione sicura da usare è strscpy().
+
+Segnaposto %p nella stringa di formato
+--------------------------------------
+
+Tradizionalmente, l'uso del segnaposto "%p" nella stringa di formato
+esponne un indirizzo di memoria in dmesg, proc, sysfs, eccetera.  Per
+evitare che questi indirizzi vengano sfruttati da malintenzionati,
+tutto gli usi di "%p" nel kernel rappresentano l'hash dell'indirizzo,
+rendendolo di fatto inutilizzabile.  Nuovi usi di "%p" non dovrebbero
+essere aggiunti al kernel.  Per una rappresentazione testuale di un
+indirizzo usate "%pS", l'output è migliore perché mostrerà il nome del
+simbolo.  Per tutto il resto, semplicemente non usate "%p".
+
+Parafrasando la `guida
+<https://lore.kernel.org/lkml/CA+55aFwQEd_d40g4mUCSsVRZzrFPUJt74vc6PPpb675hYNXcKw@mail.gmail.com/>`_
+di Linus:
+
+- Se il valore hash di "%p" è inutile, chiediti se il puntatore stesso
+  è importante. Forse dovrebbe essere rimosso del tutto?
+- Se credi davvero che il vero valore del puntatore sia importante,
+  perché alcuni stati del sistema o i livelli di privilegi di un
+  utente sono considerati "special"? Se pensi di poterlo giustificare
+  (in un commento e nel messaggio del commit) abbastanza bene da
+  affrontare il giudizio di Linus, allora forse potrai usare "%px",
+  assicurandosi anche di averne il permesso.
+
+Infine, sappi che un cambio in favore di "%p" con hash `non verrà
+accettato
+<https://lore.kernel.org/lkml/CA+55aFwieC1-nAs+NFq9RTwaR8ef9hWa4MjNBWL41F-8wM49eA@mail.gmail.com/>`_.
 
 Vettori a dimensione variabile (VLA)
 ------------------------------------
@@ -127,3 +183,47 @@ Questo può portare a dei malfunzionamenti, potrebbe sovrascrivere
 dati importanti alla fine dello stack (quando il kernel è compilato senza
 `CONFIG_THREAD_INFO_IN_TASK=y`), o sovrascrivere un pezzo di memoria adiacente
 allo stack (quando il kernel è compilato senza `CONFIG_VMAP_STACK=y`).
+
+Salto implicito nell'istruzione switch-case
+-------------------------------------------
+
+Il linguaggio C permette ai casi di un'istruzione `switch` di saltare al
+prossimo caso quando l'istruzione "break" viene omessa alla fine del caso
+corrente. Tuttavia questo rende il codice ambiguo perché non è sempre ovvio se
+l'istruzione "break" viene omessa intenzionalmente o è un baco. Per esempio,
+osservando il seguente pezzo di codice non è chiaro se lo stato
+`STATE_ONE` è stato progettato apposta per eseguire anche `STATE_TWO`::
+
+  switch (value) {
+  case STATE_ONE:
+          do_something();
+  case STATE_TWO:
+          do_other();
+          break;
+  default:
+          WARN("unknown state");
+  }
+
+Dato che c'è stata una lunga lista di problemi `dovuti alla mancanza dell'istruzione
+"break" <https://cwe.mitre.org/data/definitions/484.html>`_, oggigiorno non
+permettiamo più che vi sia un "salto implicito" (*fall-through*). Per
+identificare un salto implicito intenzionale abbiamo adottato la pseudo
+parola chiave 'fallthrough' che viene espansa nell'estensione di gcc
+`__attribute__((fallthrough))` `Statement Attributes
+<https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html>`_.
+(Quando la sintassi C17/C18 `[[fallthrough]]` sarà più comunemente
+supportata dai compilatori C, analizzatori statici, e dagli IDE,
+allora potremo usare quella sintassi per la pseudo parola chiave)
+
+Quando la sintassi [[fallthrough]] sarà più comunemente supportata dai
+compilatori, analizzatori statici, e ambienti di sviluppo IDE,
+allora potremo usarla anche noi.
+
+Ne consegue che tutti i blocchi switch/case devono finire in uno dei seguenti
+modi:
+
+* ``break;``
+* `fallthrough;``
+* ``continue;``
+* ``goto <label>;``
+* ``return [expression];``
index 224ab031ffd307216f00afd3ac08f62898037df7..89abf6d325f2321978d0c89633614d9b9f640192 100644 (file)
 .. include:: ../disclaimer-ita.rst
 
-:Original: :ref:`Documentation/process/email-clients.rst <email_clients>`
-
-.. _it_email_clients:
+:Original: :doc:`../../../process/email-clients`
+:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
 
 Informazioni sui programmi di posta elettronica per Linux
 =========================================================
 
-.. warning::
+Git
+---
+
+Oggigiorno, la maggior parte degli sviluppatori utilizza ``git send-email``
+al posto dei classici programmi di posta elettronica.  Le pagine man sono
+abbastanza buone. Dal lato del ricevente, i manutentori utilizzano ``git am``
+per applicare le patch.
+
+Se siete dei novelli utilizzatori di ``git`` allora inviate la patch a voi
+stessi. Salvatela come testo includendo tutte le intestazioni. Poi eseguite
+il comando ``git am messaggio-formato-testo.txt`` e revisionatene il risultato
+con ``git log``. Quando tutto funziona correttamente, allora potete inviare
+la patch alla lista di discussione più appropriata.
+
+Panoramica delle opzioni
+------------------------
+
+Le patch per il kernel vengono inviate per posta elettronica, preferibilmente
+come testo integrante del messaggio.  Alcuni manutentori accettano gli
+allegati, ma in questo caso gli allegati devono avere il *content-type*
+impostato come ``text/plain``.  Tuttavia, generalmente gli allegati non sono
+ben apprezzati perché rende più difficile citare porzioni di patch durante il
+processo di revisione.
+
+I programmi di posta elettronica che vengono usati per inviare le patch per il
+kernel Linux dovrebbero inviarle senza alterazioni.  Per esempio, non
+dovrebbero modificare o rimuovere tabulazioni o spazi, nemmeno all'inizio o
+alla fine delle righe.
+
+Non inviate patch con ``format=flowed``.  Questo potrebbe introdurre
+interruzioni di riga inaspettate e indesiderate.
+
+Non lasciate che il vostro programma di posta vada a capo automaticamente.
+Questo può corrompere le patch.
+
+I programmi di posta non dovrebbero modificare la codifica dei caratteri nel
+testo.  Le patch inviate per posta elettronica dovrebbero essere codificate in
+ASCII o UTF-8.
+Se configurate il vostro programma per inviare messaggi codificati con UTF-8
+eviterete possibili problemi di codifica.
+
+I programmi di posta dovrebbero generare e mantenere le intestazioni
+"References" o "In-Reply-To:" cosicché la discussione non venga interrotta.
+
+Di solito, il copia-e-incolla (o taglia-e-incolla) non funziona con le patch
+perché le tabulazioni vengono convertite in spazi.  Usando xclipboard, xclip
+e/o xcutsel potrebbe funzionare, ma è meglio che lo verifichiate o meglio
+ancora: non usate il copia-e-incolla.
+
+Non usate firme PGP/GPG nei messaggi che contengono delle patch.  Questo
+impedisce il corretto funzionamento di alcuni script per leggere o applicare
+patch (questo si dovrebbe poter correggere).
+
+Prima di inviare le patch sulle liste di discussione Linux, può essere una
+buona idea quella di inviare la patch a voi stessi, salvare il messaggio
+ricevuto, e applicarlo ai sorgenti con successo.
+
+
+Alcuni suggerimenti per i programmi di posta elettronica (MUA)
+--------------------------------------------------------------
+
+Qui troverete alcuni suggerimenti per configurare i vostri MUA allo scopo
+di modificare ed inviare patch per il kernel Linux.  Tuttavia, questi
+suggerimenti non sono da considerarsi come un riassunto di una configurazione
+completa.
+
+Legenda:
+
+- TUI = interfaccia utente testuale (*text-based user interface*)
+- GUI = interfaccia utente grafica (*graphical user interface*)
+
+Alpine (TUI)
+************
+
+Opzioni per la configurazione:
+
+Nella sezione :menuselection:`Sending Preferences`:
+
+- :menuselection:`Do Not Send Flowed Text` deve essere ``enabled``
+- :menuselection:`Strip Whitespace Before Sending` deve essere ``disabled``
+
+Quando state scrivendo un messaggio, il cursore dev'essere posizionato
+dove volete che la patch inizi, poi premendo :kbd:`CTRL-R` vi verrà chiesto
+di selezionare il file patch da inserire nel messaggio.
+
+Claws Mail (GUI)
+****************
+
+Funziona. Alcune persone riescono ad usarlo con successo per inviare le patch.
+
+Per inserire una patch usate :menuselection:`Messaggio-->Inserisci file`
+(:kbd:`CTRL-I`) oppure un editor esterno.
+
+Se la patch che avete inserito dev'essere modificata usato la finestra di
+scrittura di Claws, allora assicuratevi che l'"auto-interruzione" sia
+disabilitata :menuselection:`Configurazione-->Preferenze-->Composizione-->Interruzione riga`.
+
+Evolution (GUI)
+***************
+
+Alcune persone riescono ad usarlo con successo per inviare le patch.
+
+Quando state scrivendo una lettera selezionate: Preformattato
+  da :menuselection:`Formato-->Stile del paragrafo-->Preformattato`
+  (:kbd:`CTRL-7`) o dalla barra degli strumenti
+
+Poi per inserire la patch usate:
+:menuselection:`Inserisci--> File di testo...` (:kbd:`ALT-N x`)
+
+Potete anche eseguire ``diff -Nru old.c new.c | xclip``, selezionare
+:menuselection:`Preformattato`, e poi usare il tasto centrale del mouse.
+
+Kmail (GUI)
+***********
+
+Alcune persone riescono ad usarlo con successo per inviare le patch.
+
+La configurazione base che disabilita la composizione di messaggi HTML è
+corretta; non abilitatela.
+
+Quando state scrivendo un messaggio, nel menu opzioni, togliete la selezione a
+"A capo automatico". L'unico svantaggio sarà che qualsiasi altra cosa scriviate
+nel messaggio non verrà mandata a capo in automatico ma dovrete farlo voi.
+Il modo più semplice per ovviare a questo problema è quello di scrivere il
+messaggio con l'opzione abilitata e poi di salvarlo nelle bozze. Riaprendo ora
+il messaggio dalle bozze le andate a capo saranno parte integrante del
+messaggio, per cui togliendo l'opzione "A capo automatico" non perderete nulla.
+
+Alla fine del vostro messaggio, appena prima di inserire la vostra patch,
+aggiungete il delimitatore di patch: tre trattini (``---``).
+
+Ora, dal menu :menuselection:`Messaggio`, selezionate :menuselection:`Inserisci file di testo...`
+quindi scegliete la vostra patch.
+Come soluzione aggiuntiva potreste personalizzare la vostra barra degli
+strumenti aggiungendo un'icona per :menuselection:`Inserisci file di testo...`.
+
+Allargate la finestra di scrittura abbastanza da evitare andate a capo.
+Questo perché in Kmail 1.13.5 (KDE 4.5.4), Kmail aggiunge andate a capo
+automaticamente al momento dell'invio per tutte quelle righe che graficamente,
+nella vostra finestra di composizione, si sono estete su una riga successiva.
+Disabilitare l'andata a capo automatica non è sufficiente. Dunque, se la vostra
+patch contiene delle righe molto lunghe, allora dovrete allargare la finestra
+di composizione per evitare che quelle righe vadano a capo. Vedere:
+https://bugs.kde.org/show_bug.cgi?id=174034
+
+Potete firmare gli allegati con GPG, ma per le patch si preferisce aggiungerle
+al testo del messaggio per cui non usate la firma GPG.  Firmare le patch
+inserite come testo del messaggio le rende più difficili da estrarre dalla loro
+codifica a 7-bit.
+
+Se dovete assolutamente inviare delle patch come allegati invece di integrarle
+nel testo del messaggio, allora premete il tasto destro sull'allegato e
+selezionate :menuselection:`Proprietà`, e poi attivate
+:menuselection:`Suggerisci visualizzazione automatica` per far si che
+l'allegato sia più leggibile venendo visualizzato come parte del messaggio.
+
+Per salvare le patch inviate come parte di un messaggio, selezionate il
+messaggio che la contiene, premete il tasto destro e selezionate
+:menuselection:`Salva come`. Se il messaggio fu ben preparato, allora potrete
+usarlo interamente senza alcuna modifica.
+I messaggi vengono salvati con permessi di lettura-scrittura solo per l'utente,
+nel caso in cui vogliate copiarli altrove per renderli disponibili ad altri
+gruppi o al mondo, ricordatevi di usare ``chmod`` per cambiare i permessi.
+
+Lotus Notes (GUI)
+*****************
+
+Scappate finché potete.
+
+IBM Verse (Web GUI)
+*******************
+
+Vedi il commento per Lotus Notes.
+
+Mutt (TUI)
+**********
+
+Un sacco di sviluppatori Linux usano ``mutt``, per cui deve funzionare
+abbastanza bene.
+
+Mutt non ha un proprio editor, quindi qualunque sia il vostro editor dovrete
+configurarlo per non aggiungere automaticamente le andate a capo.  Molti
+editor hanno un'opzione :menuselection:`Inserisci file` che inserisce il
+contenuto di un file senza alterarlo.
+
+Per usare ``vim`` come editor per mutt::
+
+  set editor="vi"
+
+Se per inserire la patch nel messaggio usate xclip, scrivete il comando::
+
+  :set paste
+
+prima di premere il tasto centrale o shift-insert. Oppure usate il
+comando::
+
+  :r filename
+
+(a)llega funziona bene senza ``set paste``
+
+Potete generare le patch con ``git format-patch`` e usare Mutt per inviarle::
+
+    $ mutt -H 0001-some-bug-fix.patch
+
+Opzioni per la configurazione:
+
+Tutto dovrebbe funzionare già nella configurazione base.
+Tuttavia, è una buona idea quella di impostare ``send_charset``::
+
+   set send_charset="us-ascii:utf-8"
+
+Mutt è molto personalizzabile. Qui di seguito trovate la configurazione minima
+per iniziare ad usare Mutt per inviare patch usando Gmail::
+
+  # .muttrc
+  # ================  IMAP ====================
+  set imap_user = 'yourusername@gmail.com'
+  set imap_pass = 'yourpassword'
+  set spoolfile = imaps://imap.gmail.com/INBOX
+  set folder = imaps://imap.gmail.com/
+  set record="imaps://imap.gmail.com/[Gmail]/Sent Mail"
+  set postponed="imaps://imap.gmail.com/[Gmail]/Drafts"
+  set mbox="imaps://imap.gmail.com/[Gmail]/All Mail"
+
+  # ================  SMTP  ====================
+  set smtp_url = "smtp://username@smtp.gmail.com:587/"
+  set smtp_pass = $imap_pass
+  set ssl_force_tls = yes # Require encrypted connection
+
+  # ================  Composition  ====================
+  set editor = `echo \$EDITOR`
+  set edit_headers = yes  # See the headers when editing
+  set charset = UTF-8     # value of $LANG; also fallback for send_charset
+  # Sender, email address, and sign-off line must match
+  unset use_domain        # because joe@localhost is just embarrassing
+  set realname = "YOUR NAME"
+  set from = "username@gmail.com"
+  set use_from = yes
+
+La documentazione di Mutt contiene molte più informazioni:
+
+    https://gitlab.com/muttmua/mutt/-/wikis/UseCases/Gmail
+
+    http://www.mutt.org/doc/manual/
+
+Pine (TUI)
+**********
+
+Pine aveva alcuni problemi con gli spazi vuoti, ma questi dovrebbero essere
+stati risolti.
+
+Se potete usate alpine (il successore di pine).
+
+Opzioni di configurazione:
+
+- Nelle versioni più recenti è necessario avere ``quell-flowed-text``
+- l'opzione ``no-strip-whitespace-before-send`` è necessaria
+
+Sylpheed (GUI)
+**************
+
+- funziona bene per aggiungere testo in linea (o usando allegati)
+- permette di utilizzare editor esterni
+- è lento su cartelle grandi
+- non farà l'autenticazione TSL SMTP su una connessione non SSL
+- ha un utile righello nella finestra di scrittura
+- la rubrica non comprende correttamente il nome da visualizzare e
+  l'indirizzo associato
+
+Thunderbird (GUI)
+*****************
+
+Thunderbird è un clone di Outlook a cui piace maciullare il testo, ma esistono
+modi per impedirglielo.
+
+- permettere l'uso di editor esterni:
+  La cosa più semplice da fare con Thunderbird e le patch è quello di usare
+  l'estensione "external editor" e di usare il vostro ``$EDITOR`` preferito per
+  leggere/includere patch nel vostro messaggio.  Per farlo, scaricate ed
+  installate l'estensione e aggiungete un bottone per chiamarla rapidamente
+  usando :menuselection:`Visualizza-->Barra degli strumenti-->Personalizza...`;
+  una volta fatto potrete richiamarlo premendo sul bottone mentre siete nella
+  finestra :menuselection:`Scrivi`
+
+  Tenete presente che "external editor" richiede che il vostro editor non
+  faccia alcun fork, in altre parole, l'editor non deve ritornare prima di
+  essere stato chiuso.  Potreste dover passare dei parametri aggiuntivi al
+  vostro editor oppure cambiargli la configurazione.  Per esempio, usando
+  gvim dovrete aggiungere l'opzione -f ``/usr/bin/gvim -f`` (Se il binario
+  si trova in ``/usr/bin``) nell'apposito campo nell'interfaccia di
+  configurazione di  :menuselection:`external editor`.  Se usate altri editor
+  consultate il loro  manuale per sapere come configurarli.
+
+Per rendere l'editor interno un po' più sensato, fate così:
+
+- Modificate le impostazioni di Thunderbird per far si che non usi
+  ``format=flowed``. Andate in :menuselection:`Modifica-->Preferenze-->Avanzate-->Editor di configurazione`
+  per invocare il registro delle impostazioni.
+
+- impostate ``mailnews.send_plaintext_flowed`` a ``false``
+
+- impostate ``mailnews.wraplength`` da ``72`` a ``0``
+
+- :menuselection:`Visualizza-->Corpo del messaggio come-->Testo semplice`
+
+- :menuselection:`Visualizza-->Codifica del testo-->Unicode`
+
+
+TkRat (GUI)
+***********
+
+Funziona. Usare "Inserisci file..." o un editor esterno.
+
+Gmail (Web GUI)
+***************
+
+Non funziona per inviare le patch.
+
+Il programma web Gmail converte automaticamente i tab in spazi.
+
+Allo stesso tempo aggiunge andata a capo ogni 78 caratteri. Comunque
+il problema della conversione fra spazi e tab può essere risolto usando
+un editor esterno.
 
-    TODO ancora da tradurre
+Un altro problema è che Gmail usa la codifica base64 per tutti quei messaggi
+che contengono caratteri non ASCII. Questo include cose tipo i nomi europei.
index 012de0f3154abcafe045c970080b7fc4d4088e9d..c4c867132c885887d6ac866fd4dcd6838c3e9f4a 100644 (file)
@@ -59,6 +59,7 @@ perché non si è trovato un posto migliore.
    magic-number
    volatile-considered-harmful
    clang-format
+   ../riscv/patch-acceptance
 
 .. only::  subproject and html
 
index 07e68bfb840234820bbfc5bc6840a74cabf129cd..c709285138a7291c7ce924b6a425cdb007d239c0 100644 (file)
 .. include:: ../disclaimer-ita.rst
 
-:Original: :ref:`Documentation/process/management-style.rst <managementstyle>`
+:Original: :doc:`../../../process/management-style`
+:Translator: Alessia Mantegazza <amantegazza@vaga.pv.it>
 
-.. _it_managementstyle:
+Il modello di gestione del kernel Linux
+=======================================
 
-Tipo di gestione del kernel Linux
-=================================
+Questo breve documento descrive il modello di gestione del kernel Linux.
+Per certi versi, esso rispecchia il documento
+:ref:`translations/it_IT/process/coding-style.rst <it_codingstyle>`,
+ed è principalmente scritto per evitare di rispondere [#f1]_ in continuazione
+alle stesse identiche (o quasi) domande.
 
-.. warning::
+Il modello di gestione è qualcosa di molto personale e molto più difficile da
+qualificare rispetto a delle semplici regole di codifica, quindi questo
+documento potrebbe avere più o meno a che fare con la realtà.  È cominciato
+come un gioco, ma ciò non significa che non possa essere vero.
+Lo dovrete decidere voi stessi.
 
-    TODO ancora da tradurre
+In ogni caso, quando si parla del "dirigente del kernel", ci si riferisce
+sempre alla persona che dirige tecnicamente, e non a coloro che
+tradizionalmente hanno un ruolo direttivo all'interno delle aziende.  Se vi
+occupate di convalidare acquisti o avete una qualche idea sul budget del vostro
+gruppo, probabilmente non siete un dirigente del kernel.  Quindi i suggerimenti
+qui indicati potrebbero fare al caso vostro, oppure no.
+
+Prima di tutto, suggerirei di acquistare "Le sette regole per avere successo",
+e di non leggerlo. Bruciatelo, è un grande gesto simbolico.
+
+.. [#f1] Questo documento non fa molto per risponde alla domanda, ma rende
+        così dannatamente ovvio a chi la pone che non abbiamo la minima idea
+        di come rispondere.
+
+Comunque, partiamo:
+
+.. _it_decisions:
+
+1) Le decisioni
+---------------
+
+Tutti pensano che i dirigenti decidano, e che questo prendere decisioni
+sia importante.  Più grande e dolorosa è la decisione, più importante deve
+essere il dirigente che la prende.  Questo è molto profondo ed ovvio, ma non è
+del tutto vero.
+
+Il gioco consiste nell'"evitare" di dover prendere decisioni.  In particolare
+se qualcuno vi chiede di "Decidere" tra (a) o (b), e vi dice che ha
+davvero bisogno di voi per questo, come dirigenti siete nei guai.
+Le persone che gestite devono conoscere i dettagli più di quanto li conosciate
+voi, quindi se vengono da voi per una decisione tecnica, siete fottuti.
+Non sarete chiaramente competente per prendere quella decisione per loro.
+
+(Corollario: se le persone che gestite non conoscono i dettagli meglio di voi,
+anche in questo caso sarete fregati, tuttavia per altre ragioni.  Ossia state
+facendo il lavoro sbagliato, e che invece dovrebbero essere "loro" a gestirvi)
+
+Quindi il gioco si chiama "evitare" decisioni, almeno le più grandi e
+difficili.  Prendere decisioni piccoli e senza conseguenze va bene, e vi fa
+sembrare competenti in quello che state facendo, quindi quello che un dirigente
+del kernel ha bisogno di fare è trasformare le decisioni grandi e difficili
+in minuzie delle quali nessuno importa.
+
+Ciò aiuta a capire che la differenza chiave tra una grande decisione ed una
+piccola sta nella possibilità di modificare tale decisione in seguito.
+Qualsiasi decisione importante può essere ridotta in decisioni meno importanti,
+ma dovete assicurarvi che possano essere reversibili in caso di errori
+(presenti o futuri).  Improvvisamente, dovrete essere doppiamente dirigenti
+per **due** decisioni non sequenziali - quella sbagliata **e** quella giusta.
+
+E le persone vedranno tutto ciò come prova di vera capacità di comando
+(*cough* cavolata *cough*)
+
+Così la chiave per evitare le decisioni difficili diviene l'evitare
+di fare cose che non possono essere disfatte.  Non infilatevi in un angolo
+dal quale non potrete sfuggire.  Un topo messo all'angolo può rivelarsi
+pericoloso - un dirigente messo all'angolo è solo pietoso.
+
+**In ogni caso** dato che nessuno è stupido al punto da lasciare veramente ad
+un dirigente del kernel un enorme responsabilità, solitamente è facile fare
+marcia indietro. Annullare una decisione è molto facile: semplicemente dite a
+tutti che siete stati degli scemi incompetenti, dite che siete dispiaciuti, ed
+annullate tutto l'inutile lavoro sul quale gli altri hanno lavorato nell'ultimo
+anno.  Improvvisamente la decisione che avevate preso un anno fa non era poi
+così grossa, dato che può essere facilmente annullata.
+
+È emerso che alcune persone hanno dei problemi con questo tipo di approccio,
+questo per due ragioni:
+
+ - ammettere di essere degli idioti è più difficile di quanto sembri.  A tutti
+   noi piace mantenere le apparenze, ed uscire allo scoperto in pubblico per
+   ammettere che ci si è sbagliati è qualcosa di davvero impegnativo.
+ - avere qualcuno che ti dice che ciò su cui hai lavorato nell'ultimo anno
+   non era del tutto valido, può rivelarsi difficile anche per un povero ed
+   umile ingegnere, e mentre il **lavoro** vero era abbastanza facile da
+   cancellare, dall'altro canto potreste aver irrimediabilmente perso la
+   fiducia di quell'ingegnere.  E ricordate che l'"irrevocabile" era quello
+   che avevamo cercato di evitare fin dall'inizio, e la vostra decisione
+   ha finito per esserlo.
+
+Fortunatamente, entrambe queste ragioni posso essere mitigate semplicemente
+ammettendo fin dal principio che non avete una cavolo di idea, dicendo
+agli altri in anticipo che la vostra decisione è puramente ipotetica, e che
+potrebbe essere sbagliata.  Dovreste sempre riservarvi il diritto di cambiare
+la vostra opinione, e rendere gli altri ben **consapevoli** di ciò.
+Ed è molto più facile ammettere di essere stupidi quando non avete **ancora**
+fatto quella cosa stupida.
+
+Poi, quando è realmente emersa la vostra stupidità, le persone semplicemente
+roteeranno gli occhi e diranno "Uffa, no, ancora".
+
+Questa ammissione preventiva di incompetenza potrebbe anche portare le persone
+che stanno facendo il vero lavoro, a pensarci due volte.  Dopo tutto, se
+**loro** non sono certi se sia una buona idea, voi, sicuro come la morte,
+non dovreste incoraggiarli promettendogli che ciò su cui stanno lavorando
+verrà incluso.  Fate si che ci pensino due volte prima che si imbarchino in un
+grosso lavoro.
+
+Ricordate: loro devono sapere più cose sui dettagli rispetto a voi, e
+solitamente pensano di avere già la risposta a tutto. La miglior cosa che
+potete fare in qualità di dirigente è di non instillare troppa fiducia, ma
+invece fornire una salutare dose di pensiero critico su quanto stanno facendo.
+
+Comunque, un altro modo di evitare una decisione è quello di lamentarsi
+malinconicamente dicendo : "non possiamo farli entrambi e basta?" e con uno
+sguardo pietoso.  Fidatevi, funziona.  Se non è chiaro quale sia il miglior
+approccio, lo scopriranno.  La risposta potrebbe essere data dal fatto che
+entrambe i gruppi di lavoro diventano frustati al punto di rinunciarvi.
+
+Questo può suonare come un fallimento, ma di solito questo è un segno che
+c'era qualcosa che non andava in entrambe i progetti, e il motivo per
+il quale le persone coinvolte non abbiano potuto decidere era che entrambe
+sbagliavano.  Voi ne uscirete freschi come una rosa, e avrete evitato un'altra
+decisione con la quale avreste potuto fregarvi.
+
+
+2) Le persone
+-------------
+
+Ci sono molte persone stupide, ed essere un dirigente significa che dovrete
+scendere a patti con questo, e molto più importate, che **loro** devono avere
+a che fare con **voi**.
+
+Ne emerge che mentre è facile annullare degli errori tecnici, non è invece
+così facile rimuovere i disordini della personalità.  Dovrete semplicemente
+convivere con i loro, ed i vostri, problemi.
+
+Comunque, al fine di preparavi in qualità di dirigenti del kernel, è meglio
+ricordare di non abbattere alcun ponte, bombardare alcun paesano innocente,
+o escludere troppi sviluppatori kernel. Ne emerge che escludere le persone
+è piuttosto facile, mentre includerle nuovamente è difficile. Così
+"l'esclusione" immediatamente cade sotto il titolo di "non reversibile", e
+diviene un no-no secondo la sezione :ref:`it_decisions`.
+
+Esistono alcune semplici regole qui:
+
+ (1) non chiamate le persone teste di c*** (al meno, non in pubblico)
+ (2) imparate a scusarvi quando dimenticate la regola (1)
+
+Il problema del punto numero 1 è che è molto facile da rispettare, dato che
+è possibile dire "sei una testa di c***" in milioni di modi differenti [#f2]_,
+a volte senza nemmeno pensarci, e praticamente sempre con la calda convinzione
+di essere nel giusto.
+
+E più convinti sarete che avete ragione (e diciamolo, potete chiamare
+praticamente **tutti** testa di c**, e spesso **sarete** nel giusto), più
+difficile sarà scusarvi successivamente.
+
+Per risolvere questo problema, avete due possibilità:
+
+ - diventare davvero bravi nello scusarsi
+ - essere amabili così che nessuno finirà col sentirsi preso di mira.  Siate
+   creativi abbastanza, e potrebbero esserne divertiti.
+
+L'opzione dell'essere immancabilmente educati non esiste proprio. Nessuno
+si fiderà di qualcuno che chiaramente sta nascondendo il suo vero carattere.
+
+.. [#f2] Paul Simon cantava: "50 modi per lasciare il vostro amante", perché,
+        molto francamente, "Un milione di modi per dire ad uno sviluppatore
+        Testa di c***" non avrebbe funzionato. Ma sono sicuro che ci abbia
+        pensato.
+
+
+3) Le persone II - quelle buone
+-------------------------------
+
+Mentre emerge che la maggior parte delle persone sono stupide, il corollario
+a questo è il triste fatto che anche voi siete fra queste, e che mentre
+possiamo tutti crogiolarci nella sicurezza di essere migliori della media
+delle persone (diciamocelo, nessuno crede di essere nelle media o sotto di
+essa), dovremmo anche ammettere che non siamo il "coltello più affilato" del
+circondario, e che ci saranno altre persone che sono meno stupide di quanto
+lo siete voi.
+
+Molti reagiscono male davanti alle persone intelligenti. Altri le usano a
+proprio vantaggio.
+
+Assicuratevi che voi, in qualità di manutentori del kernel, siate nel secondo
+gruppo. Inchinatevi dinanzi a loro perché saranno le persone che vi renderanno
+il lavoro più facile.  In particolare, prenderanno le decisioni per voi, che è
+l'oggetto di questo gioco.
+
+Quindi quando trovate qualcuno più sveglio di voi, prendetevela comoda.
+Le vostre responsabilità dirigenziali si ridurranno in gran parte nel dire
+"Sembra una buona idea - Vai", oppure "Sembra buono, ma invece circa questo e
+quello?".  La seconda versione in particolare è una gran modo per imparare
+qualcosa di nuovo circa "questo e quello" o di sembrare **extra** dirigenziali
+sottolineando qualcosa alla quale i più svegli non avevano pensato.  In
+entrambe i casi, vincete.
+
+Una cosa alla quale dovete fare attenzione è che l'essere grandi in qualcosa
+non si traduce automaticamente nell'essere grandi anche in altre cose.  Quindi
+dovreste dare una spintarella alle persone in una specifica direzione, ma
+diciamocelo, potrebbero essere bravi in ciò che fanno e far schifo in tutto
+il resto.  La buona notizia è che le persone tendono a gravitare attorno a ciò
+in cui sono bravi, quindi non state facendo nulla di irreversibile quando li
+spingete verso una certa direzione, solo non spingete troppo.
+
+
+4) Addossare le colpe
+---------------------
+
+Le cose andranno male, e le persone vogliono qualcuno da incolpare. Sarete voi.
+
+Non è poi così difficile accettare la colpa, specialmente se le persone
+riescono a capire che non era **tutta** colpa vostra.  Il che ci porta
+sulla miglior strada per assumersi la colpa: fatelo per qualcun'altro.
+Vi sentirete bene nel assumervi la responsabilità, e loro si sentiranno
+bene nel non essere incolpati, e coloro che hanno perso i loro 36GB di
+pornografia a causa della vostra incompetenza ammetteranno a malincuore che
+almeno non avete cercato di fare il furbetto.
+
+Successivamente fate in modo che gli sviluppatori che in realtà hanno fallito
+(se riuscite a trovarli) sappiano **in privato** che sono "fottuti".
+Questo non per fargli sapere che la prossima volta possono evitarselo ma per
+fargli capire che sono in debito.  E, forse cosa più importante, sono loro che
+devono sistemare la cosa.  Perché, ammettiamolo, è sicuro non sarete voi a
+farlo.
+
+Assumersi la colpa è anche ciò che vi rendere dirigenti in prima battuta.
+È parte di ciò che spinge gli altri a fidarsi di voi, e vi garantisce
+la gloria potenziale, perché siete gli unici a dire "Ho fatto una cavolata".
+E se avete seguito le regole precedenti, sarete decisamente bravi nel dirlo.
+
+
+5) Le cose da evitare
+---------------------
+
+Esiste una cosa che le persone odiano più che essere chiamate "teste di c****",
+ed è essere chiamate "teste di c****" con fare da bigotto.  Se per il primo
+caso potrete comunque scusarvi, per il secondo non ve ne verrà data nemmeno
+l'opportunità.  Probabilmente smetteranno di ascoltarvi anche se tutto sommato
+state svolgendo un buon lavoro.
+
+Tutti crediamo di essere migliori degli altri, il che significa che quando
+qualcuno inizia a darsi delle arie, ci da **davvero** fastidio.  Potreste anche
+essere moralmente ed intellettualmente superiore a tutti quelli attorno a voi,
+ma non cercate di renderlo ovvio per gli altri a meno che non **vogliate**
+veramente far arrabbiare qualcuno [#f3]_.
+
+Allo stesso modo evitate di essere troppo gentili e pacati.  Le buone maniere
+facilmente finiscono per strabordare e nascondere i problemi, e come si usa
+dire, "su internet nessuno può sentire la vostra pacatezza".  Usate argomenti
+diretti per farvi capire, non potete sperare che la gente capisca in altro
+modo.
+
+Un po' di umorismo può aiutare a smorzare sia la franchezza che la moralità.
+Andare oltre i limiti al punto d'essere ridicolo può portare dei punti a casa
+senza renderlo spiacevole per i riceventi, i quali penseranno che stavate
+facendo gli scemi.  Può anche aiutare a lasciare andare quei blocchi mentali
+che abbiamo nei confronti delle critiche.
+
+.. [#f3] Suggerimento: i forum di discussione su internet, che non sono
+  collegati col vostro lavoro, sono ottimi modi per sfogare la frustrazione
+  verso altre persone. Di tanto in tanto scrivete messaggi offensivi col ghigno
+  in faccia per infiammare qualche discussione: vi sentirete purificati. Solo
+  cercate di non cagare troppo vicino a casa.
+
+6) Perché io?
+-------------
+
+Dato che la vostra responsabilità principale è quella di prendervi le colpe
+d'altri, e rendere dolorosamente ovvio a tutti che siete degli incompetenti,
+la domanda naturale che ne segue sarà : perché dovrei fare tutto ciò?
+
+Innanzitutto, potreste diventare o no popolari al punto da avere la fila di
+ragazzine (o ragazzini, evitiamo pregiudizi o sessismo) che gridano e bussano
+alla porta del vostro camerino, ma comunque **proverete** un immenso senso di
+realizzazione personale dall'essere "in carica".  Dimenticate il fatto che voi
+state discutendo con tutti e che cercate di inseguirli il più velocemente che
+potete. Tutti continueranno a pensare che voi siete la persona in carica.
+
+È un bel lavoro se riuscite ad adattarlo a voi.
index 995ee69fab119237d8619c4d9a2cbc0a05052cae..3e575502690fa1f1f019e841acbf42e282a30f62 100644 (file)
@@ -117,7 +117,7 @@ sottomissione delle patch, in particolare
     sorgenti che ne spieghi la logica: cosa fanno e perché.
 
 25) Se la patch aggiunge nuove chiamate ioctl, allora aggiornate
-    ``Documentation/ioctl/ioctl-number.rst``.
+    ``Documentation/userspace-api/ioctl/ioctl-number.rst``.
 
 26) Se il codice che avete modificato dipende o usa una qualsiasi interfaccia o
     funzionalità del kernel che è associata a uno dei seguenti simboli
diff --git a/Documentation/translations/it_IT/riscv/patch-acceptance.rst b/Documentation/translations/it_IT/riscv/patch-acceptance.rst
new file mode 100644 (file)
index 0000000..edf6725
--- /dev/null
@@ -0,0 +1,40 @@
+.. include:: ../disclaimer-ita.rst
+
+:Original: :doc:`../../../riscv/patch-acceptance`
+:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
+
+arch/riscv linee guida alla manutenzione per gli sviluppatori
+=============================================================
+
+Introduzione
+------------
+
+L'insieme di istruzioni RISC-V sono sviluppate in modo aperto: le
+bozze in fase di sviluppo sono disponibili a tutti per essere
+revisionate e per essere sperimentare nelle implementazioni.  Le bozze
+dei nuovi moduli o estensioni possono cambiare in fase di sviluppo - a
+volte in modo incompatibile rispetto a bozze precedenti.  Questa
+flessibilità può portare a dei problemi di manutenzioni per il
+supporto RISC-V nel kernel Linux. I manutentori Linux non amano
+l'abbandono del codice, e il processo di sviluppo del kernel
+preferisce codice ben revisionato e testato rispetto a quello
+sperimentale.  Desideriamo estendere questi stessi principi al codice
+relativo all'architettura RISC-V che verrà accettato per l'inclusione
+nel kernel.
+
+In aggiunta alla lista delle verifiche da fare prima di inviare una patch
+-------------------------------------------------------------------------
+
+Accetteremo le patch per un nuovo modulo o estensione se la fondazione
+RISC-V li classifica come "Frozen" o "Retified".  (Ovviamente, gli
+sviluppatori sono liberi di mantenere una copia del kernel Linux
+contenente il codice per una bozza di estensione).
+
+In aggiunta, la specifica RISC-V permette agli implementatori di
+creare le proprie estensioni.  Queste estensioni non passano
+attraverso il processo di revisione della fondazione RISC-V.  Per
+questo motivo, al fine di evitare complicazioni o problemi di
+prestazioni, accetteremo patch solo per quelle estensioni che sono
+state ufficialmente accettate dalla fondazione RISC-V.  (Ovviamente,
+gli implementatori sono liberi di mantenere una copia del kernel Linux
+contenente il codice per queste specifiche estensioni).
index 2e831ece6e26b1e5155164ccdaee2cc8de4595a7..e50fe654133502ad837d6c3a7cdb83cf86f99407 100644 (file)
@@ -641,7 +641,7 @@ P 는 짝수 번호 캐시 라인에 저장되어 있고, 변수 B 는 홀수 
 리눅스 커널이 지원하는 CPU 들은 (1) 쓰기가 정말로 일어날지, (2) 쓰기가 어디에
 이루어질지, 그리고 (3) 쓰여질 값을 확실히 알기 전까지는 쓰기를 수행하지 않기
 때문입니다.  하지만 "컨트롤 의존성" 섹션과
-Documentation/RCU/rcu_dereference.txt 파일을 주의 깊게 읽어 주시기 바랍니다:
+Documentation/RCU/rcu_dereference.rst 파일을 주의 깊게 읽어 주시기 바랍니다:
 컴파일러는 매우 창의적인 많은 방법으로 종속성을 깰 수 있습니다.
 
        CPU 1                 CPU 2
index 956026d5cf8264a3343c51afbbec6403f2ae7181..9aec8dca4fcf18b46c5c0fedd9cbb2bf5878606e 100644 (file)
@@ -1,4 +1,4 @@
-Chinese translated version of Documentation/IRQ.txt
+Chinese translated version of Documentation/core-api/irq/index.rst
 
 If you have any comment or update to the content, please contact the
 original document maintainer directly.  However, if you have a problem
@@ -9,7 +9,7 @@ or if there is a problem with the translation.
 Maintainer: Eric W. Biederman <ebiederman@xmission.com>
 Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
 ---------------------------------------------------------------------
-Documentation/IRQ.txt 的中文翻译
+Documentation/core-api/irq/index.rst 的中文翻译
 
 如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
diff --git a/Documentation/translations/zh_CN/filesystems/debugfs.rst b/Documentation/translations/zh_CN/filesystems/debugfs.rst
new file mode 100644 (file)
index 0000000..f8a2879
--- /dev/null
@@ -0,0 +1,221 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: :ref:`Documentation/filesystems/debugfs.txt <debugfs_index>`
+
+=======
+Debugfs
+=======
+
+译者
+::
+
+       中文版维护者: 罗楚成 Chucheng Luo <luochucheng@vivo.com>
+       中文版翻译者: 罗楚成 Chucheng Luo <luochucheng@vivo.com>
+       中文版校译者:  罗楚成 Chucheng Luo <luochucheng@vivo.com>
+
+
+
+版权所有2020 罗楚成 <luochucheng@vivo.com>
+
+
+Debugfs是内核开发人员在用户空间获取信息的简单方法。与/proc不同,proc只提供进程
+信息。也不像sysfs,具有严格的“每个文件一个值“的规则。debugfs根本没有规则,开发
+人员可以在这里放置他们想要的任何信息。debugfs文件系统也不能用作稳定的ABI接口。
+从理论上讲,debugfs导出文件的时候没有任何约束。但是[1]实际情况并不总是那么
+简单。即使是debugfs接口,也最好根据需要进行设计,并尽量保持接口不变。
+
+
+Debugfs通常使用以下命令安装::
+
+    mount -t debugfs none /sys/kernel/debug
+
+(或等效的/etc/fstab行)。
+debugfs根目录默认仅可由root用户访问。要更改对文件树的访问,请使用“ uid”,“ gid”
+和“ mode”挂载选项。请注意,debugfs API仅按照GPL协议导出到模块。
+
+使用debugfs的代码应包含<linux/debugfs.h>。然后,首先是创建至少一个目录来保存
+一组debugfs文件::
+
+    struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
+
+如果成功,此调用将在指定的父目录下创建一个名为name的目录。如果parent参数为空,
+则会在debugfs根目录中创建。创建目录成功时,返回值是一个指向dentry结构体的指针。
+该dentry结构体的指针可用于在目录中创建文件(以及最后将其清理干净)。ERR_PTR
+(-ERROR)返回值表明出错。如果返回ERR_PTR(-ENODEV),则表明内核是在没有debugfs
+支持的情况下构建的,并且下述函数都不会起作用。
+
+在debugfs目录中创建文件的最通用方法是::
+
+    struct dentry *debugfs_create_file(const char *name, umode_t mode,
+                                      struct dentry *parent, void *data,
+                                      const struct file_operations *fops);
+
+在这里,name是要创建的文件的名称,mode描述了访问文件应具有的权限,parent指向
+应该保存文件的目录,data将存储在产生的inode结构体的i_private字段中,而fops是
+一组文件操作函数,这些函数中实现文件操作的具体行为。至少,read()和/或
+write()操作应提供;其他可以根据需要包括在内。同样的,返回值将是指向创建文件
+的dentry指针,错误时返回ERR_PTR(-ERROR),系统不支持debugfs时返回值为ERR_PTR
+(-ENODEV)。创建一个初始大小的文件,可以使用以下函数代替::
+
+    struct dentry *debugfs_create_file_size(const char *name, umode_t mode,
+                               struct dentry *parent, void *data,
+                               const struct file_operations *fops,
+                               loff_t file_size);
+
+file_size是初始文件大小。其他参数跟函数debugfs_create_file的相同。
+
+在许多情况下,没必要自己去创建一组文件操作;对于一些简单的情况,debugfs代码提供
+了许多帮助函数。包含单个整数值的文件可以使用以下任何一项创建::
+
+    void debugfs_create_u8(const char *name, umode_t mode,
+                          struct dentry *parent, u8 *value);
+    void debugfs_create_u16(const char *name, umode_t mode,
+                           struct dentry *parent, u16 *value);
+    struct dentry *debugfs_create_u32(const char *name, umode_t mode,
+                                     struct dentry *parent, u32 *value);
+    void debugfs_create_u64(const char *name, umode_t mode,
+                           struct dentry *parent, u64 *value);
+
+这些文件支持读取和写入给定值。如果某个文件不支持写入,只需根据需要设置mode
+参数位。这些文件中的值以十进制表示;如果需要使用十六进制,可以使用以下函数
+替代::
+
+    void debugfs_create_x8(const char *name, umode_t mode,
+                          struct dentry *parent, u8 *value);
+    void debugfs_create_x16(const char *name, umode_t mode,
+                           struct dentry *parent, u16 *value);
+    void debugfs_create_x32(const char *name, umode_t mode,
+                           struct dentry *parent, u32 *value);
+    void debugfs_create_x64(const char *name, umode_t mode,
+                           struct dentry *parent, u64 *value);
+
+这些功能只有在开发人员知道导出值的大小的时候才有用。某些数据类型在不同的架构上
+有不同的宽度,这样会使情况变得有些复杂。在这种特殊情况下可以使用以下函数::
+
+    void debugfs_create_size_t(const char *name, umode_t mode,
+                              struct dentry *parent, size_t *value);
+
+不出所料,此函数将创建一个debugfs文件来表示类型为size_t的变量。
+
+同样地,也有导出无符号长整型变量的函数,分别以十进制和十六进制表示如下::
+
+    struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
+                                       struct dentry *parent,
+                                       unsigned long *value);
+    void debugfs_create_xul(const char *name, umode_t mode,
+                           struct dentry *parent, unsigned long *value);
+
+布尔值可以通过以下方式放置在debugfs中::
+
+    struct dentry *debugfs_create_bool(const char *name, umode_t mode,
+                                      struct dentry *parent, bool *value);
+
+
+读取结果文件将产生Y(对于非零值)或N,后跟换行符写入的时候,它只接受大写或小写
+值或1或0。任何其他输入将被忽略。
+
+同样,atomic_t类型的值也可以放置在debugfs中::
+
+    void debugfs_create_atomic_t(const char *name, umode_t mode,
+                                struct dentry *parent, atomic_t *value)
+
+读取此文件将获得atomic_t值,写入此文件将设置atomic_t值。
+
+另一个选择是通过以下结构体和函数导出一个任意二进制数据块::
+
+    struct debugfs_blob_wrapper {
+       void *data;
+       unsigned long size;
+    };
+
+    struct dentry *debugfs_create_blob(const char *name, umode_t mode,
+                                      struct dentry *parent,
+                                      struct debugfs_blob_wrapper *blob);
+
+读取此文件将返回由指针指向debugfs_blob_wrapper结构体的数据。一些驱动使用“blobs”
+作为一种返回几行(静态)格式化文本的简单方法。这个函数可用于导出二进制信息,但
+似乎在主线中没有任何代码这样做。请注意,使用debugfs_create_blob()命令创建的
+所有文件是只读的。
+
+如果您要转储一个寄存器块(在开发过程中经常会这么做,但是这样的调试代码很少上传
+到主线中。Debugfs提供两个函数:一个用于创建仅寄存器文件,另一个把一个寄存器块
+插入一个顺序文件中::
+
+    struct debugfs_reg32 {
+       char *name;
+       unsigned long offset;
+    };
+
+    struct debugfs_regset32 {
+       struct debugfs_reg32 *regs;
+       int nregs;
+       void __iomem *base;
+    };
+
+    struct dentry *debugfs_create_regset32(const char *name, umode_t mode,
+                                    struct dentry *parent,
+                                    struct debugfs_regset32 *regset);
+
+    void debugfs_print_regs32(struct seq_file *s, struct debugfs_reg32 *regs,
+                        int nregs, void __iomem *base, char *prefix);
+
+“base”参数可能为0,但您可能需要使用__stringify构建reg32数组,实际上有许多寄存器
+名称(宏)是寄存器块在基址上的字节偏移量。
+
+如果要在debugfs中转储u32数组,可以使用以下函数创建文件::
+
+     void debugfs_create_u32_array(const char *name, umode_t mode,
+                       struct dentry *parent,
+                       u32 *array, u32 elements);
+
+“array”参数提供数据,而“elements”参数为数组中元素的数量。注意:数组创建后,数组
+大小无法更改。
+
+有一个函数来创建与设备相关的seq_file::
+
+   struct dentry *debugfs_create_devm_seqfile(struct device *dev,
+                               const char *name,
+                               struct dentry *parent,
+                               int (*read_fn)(struct seq_file *s,
+                                       void *data));
+
+“dev”参数是与此debugfs文件相关的设备,并且“read_fn”是一个函数指针,这个函数在
+打印seq_file内容的时候被回调。
+
+还有一些其他的面向目录的函数::
+
+    struct dentry *debugfs_rename(struct dentry *old_dir,
+                                 struct dentry *old_dentry,
+                                 struct dentry *new_dir,
+                                 const char *new_name);
+
+    struct dentry *debugfs_create_symlink(const char *name,
+                                          struct dentry *parent,
+                                          const char *target);
+
+调用debugfs_rename()将为现有的debugfs文件重命名,可能同时切换目录。 new_name
+函数调用之前不能存在;返回值为old_dentry,其中包含更新的信息。可以使用
+debugfs_create_symlink()创建符号链接。
+
+所有debugfs用户必须考虑的一件事是:
+
+debugfs不会自动清除在其中创建的任何目录。如果一个模块在不显式删除debugfs目录的
+情况下卸载模块,结果将会遗留很多野指针,从而导致系统不稳定。因此,所有debugfs
+用户-至少是那些可以作为模块构建的用户-必须做模块卸载的时候准备删除在此创建的
+所有文件和目录。一份文件可以通过以下方式删除::
+
+    void debugfs_remove(struct dentry *dentry);
+
+dentry值可以为NULL或错误值,在这种情况下,不会有任何文件被删除。
+
+很久以前,内核开发者使用debugfs时需要记录他们创建的每个dentry指针,以便最后所有
+文件都可以被清理掉。但是,现在debugfs用户能调用以下函数递归清除之前创建的文件::
+
+    void debugfs_remove_recursive(struct dentry *dentry);
+
+如果将对应顶层目录的dentry传递给以上函数,则该目录下的整个层次结构将会被删除。
+
+注释:
+[1] http://lwn.net/Articles/309298/
index 14f155edaf697b8c6539a36171a38f3ac1197934..186501d13bc1be51f93130af4d04ec86737d423d 100644 (file)
@@ -24,4 +24,5 @@ Linux Kernel中的文件系统
    :maxdepth: 2
 
    virtiofs
+   debugfs
 
index ee1f37da5b234394b3034058fc0deb886a8dfc1b..fcf620049d11116ada135a642c940513ff272529 100644 (file)
@@ -1,4 +1,4 @@
-Chinese translated version of Documentation/filesystems/sysfs.txt
+Chinese translated version of Documentation/filesystems/sysfs.rst
 
 If you have any comment or update to the content, please contact the
 original document maintainer directly.  However, if you have a problem
@@ -10,7 +10,7 @@ Maintainer: Patrick Mochel    <mochel@osdl.org>
                Mike Murphy <mamurph@cs.clemson.edu>
 Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
 ---------------------------------------------------------------------
-Documentation/filesystems/sysfs.txt 的中文翻译
+Documentation/filesystems/sysfs.rst 的中文翻译
 
 如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
@@ -40,7 +40,7 @@ sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导
 数据结构及其属性,以及它们之间的关联到用户空间的方法。
 
 sysfs 始终与 kobject 的底层结构紧密相关。请阅读
-Documentation/kobject.txt 文档以获得更多关于 kobject 接口的
+Documentation/core-api/kobject.rst 文档以获得更多关于 kobject 接口的
 信息。
 
 
@@ -281,7 +281,7 @@ drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序
 假定驱动没有跨越多个总线类型)。
 
 fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须
-在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.txt)。
+在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.rst)。
 
 dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以
 <major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录
index 8738c55e42a2627cb246b35b729996120555d1f4..50386e0e42e7fd6a629e612c5743771efa2de24c 100644 (file)
@@ -97,7 +97,7 @@ Linux内核补丁提交清单
 24) 所有内存屏障例如 ``barrier()``, ``rmb()``, ``wmb()`` 都需要源代码中的注
     释来解释它们正在执行的操作及其原因的逻辑。
 
-25) 如果补丁添加了任何ioctl,那么也要更新 ``Documentation/ioctl/ioctl-number.rst``
+25) 如果补丁添加了任何ioctl,那么也要更新 ``Documentation/userspace-api/ioctl/ioctl-number.rst``
 
 26) 如果修改后的源代码依赖或使用与以下 ``Kconfig`` 符号相关的任何内核API或
     功能,则在禁用相关 ``Kconfig`` 符号和/或 ``=m`` (如果该选项可用)的情况
index 9c39ee58ea507263f8c48efe7005a9feca5faaeb..a96abcdec777e40c9d9c9af122adf8a02cbcf8f6 100644 (file)
@@ -488,7 +488,7 @@ struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
 
 这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL),
 并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address)
-作为参数调用 i2c_new_device()。如果一切顺利,则就在 v4l2_device
+作为参数调用 i2c_new_client_device()。如果一切顺利,则就在 v4l2_device
 中注册了子设备。
 
 你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的
index 54fb08baae22c56374e2e10e7b2fe245b0f8605e..158e48dab586d1438807c693e014b05fa4ed541f 100644 (file)
@@ -24,7 +24,7 @@ Linux provides a number of functions for gadgets to use.
 Creating a gadget means deciding what configurations there will be
 and which functions each configuration will provide.
 
-Configfs (please see `Documentation/filesystems/configfs/*`) lends itself nicely
+Configfs (please see `Documentation/filesystems/configfs.rst`) lends itself nicely
 for the purpose of telling the kernel about the above mentioned decision.
 This document is about how to do it.
 
@@ -354,7 +354,7 @@ the directories in general can be named at will. A group can have
 a number of its default sub-groups created automatically.
 
 For more information on configfs please see
-`Documentation/filesystems/configfs/*`.
+`Documentation/filesystems/configfs.rst`.
 
 The concepts described above translate to USB gadgets like this:
 
index f759edafd938a5b8fb27bfc74b9813e01e7e8d51..52bf58417653b3c1812c15ab614a1f90b61c6973 100644 (file)
@@ -146,6 +146,7 @@ Code  Seq#    Include File                                           Comments
 'H'   40-4F  sound/hdspm.h                                           conflict!
 'H'   40-4F  sound/hdsp.h                                            conflict!
 'H'   90     sound/usb/usx2y/usb_stream.h
+'H'   00-0F  uapi/misc/habanalabs.h                                  conflict!
 'H'   A0     uapi/linux/usb/cdc-wdm.h
 'H'   C0-F0  net/bluetooth/hci.h                                     conflict!
 'H'   C0-DF  net/bluetooth/hidp/hidp.h                               conflict!
index c3129b9ba5cb43a7b4b21e30b9c9dba044714856..57c01f531e612273ff55d77627bd5768bcd56694 100644 (file)
@@ -74,7 +74,7 @@ should point to a file descriptor that is opened on the ``/dev/sev``
 device, if needed (see individual commands).
 
 On output, ``error`` is zero on success, or an error code.  Error codes
-are defined in ``<linux/psp-dev.h>`.
+are defined in ``<linux/psp-dev.h>``.
 
 KVM implements the following commands to support common lifecycle events of SEV
 guests, such as launching, running, snapshotting, migrating and decommissioning.
index efbbe570aa9b7b3bf581e89e1508fb611294bf81..d2c1cbce1018de6656048c07847141a1d336440e 100644 (file)
@@ -2572,13 +2572,15 @@ list in 4.68.
 :Parameters: None
 :Returns: 0 on success, -1 on error
 
-This signals to the host kernel that the specified guest is being paused by
-userspace.  The host will set a flag in the pvclock structure that is checked
-from the soft lockup watchdog.  The flag is part of the pvclock structure that
-is shared between guest and host, specifically the second bit of the flags
+This ioctl sets a flag accessible to the guest indicating that the specified
+vCPU has been paused by the host userspace.
+
+The host will set a flag in the pvclock structure that is checked from the
+soft lockup watchdog.  The flag is part of the pvclock structure that is
+shared between guest and host, specifically the second bit of the flags
 field of the pvclock_vcpu_time_info structure.  It will be set exclusively by
 the host and read/cleared exclusively by the guest.  The guest operation of
-checking and clearing the flag must an atomic operation so
+checking and clearing the flag must be an atomic operation so
 load-link/store-conditional, or equivalent must be used.  There are two cases
 where the guest will clear the flag: when the soft lockup watchdog timer resets
 itself or when a soft lockup is detected.  This ioctl can be called any time
index 2357dd2d865537c6af42f8afa3f2d398bd661e47..687b60d76ca904f87fa9ba3183bb8135149dd1cf 100644 (file)
@@ -76,5 +76,5 @@ It is advisable that one or more 64k pages are set aside for the purpose of
 these structures and not used for other purposes, this enables the guest to map
 the region using 64k pages and avoids conflicting attributes with other memory.
 
-For the user space interface see Documentation/virt/kvm/devices/vcpu.txt
+For the user space interface see Documentation/virt/kvm/devices/vcpu.rst
 section "3. GROUP: KVM_ARM_VCPU_PVTIME_CTRL".
index 9963e680770a1a0e0d74e88b3cdd84d24be8c38d..ca374d3fe085e7e673a163a33778281dfd4b5fa4 100644 (file)
@@ -110,5 +110,5 @@ Returns:
 
 Specifies the base address of the stolen time structure for this VCPU. The
 base address must be 64 byte aligned and exist within a valid guest memory
-region. See Documentation/virt/kvm/arm/pvtime.txt for more information
+region. See Documentation/virt/kvm/arm/pvtime.rst for more information
 including the layout of the stolen time structure.
index dbaf207e560d0f71c6a2f008e5958796034ff068..ed4fddd364ea5273a485b6390b3e2e98994f04d4 100644 (file)
@@ -22,7 +22,7 @@ S390:
   number in R1.
 
   For further information on the S390 diagnose call as supported by KVM,
-  refer to Documentation/virt/kvm/s390-diag.txt.
+  refer to Documentation/virt/kvm/s390-diag.rst.
 
 PowerPC:
   It uses R3-R10 and hypercall number in R11. R4-R11 are used as output registers.
@@ -30,7 +30,7 @@ PowerPC:
 
   KVM hypercalls uses 4 byte opcode, that are patched with 'hypercall-instructions'
   property inside the device tree's /hypervisor node.
-  For more information refer to Documentation/virt/kvm/ppc-pv.txt
+  For more information refer to Documentation/virt/kvm/ppc-pv.rst
 
 MIPS:
   KVM hypercalls use the HYPCALL instruction with code 0 and the hypercall
index 60981887d20b847ee5fa71719a04d44e6d2b8a65..46126ecc70f7a1e8e67b4acd9f1c33e684f6f3d2 100644 (file)
@@ -319,7 +319,7 @@ Handling a page fault is performed as follows:
 
  - If both P bit and R/W bit of error code are set, this could possibly
    be handled as a "fast page fault" (fixed without taking the MMU lock).  See
-   the description in Documentation/virt/kvm/locking.txt.
+   the description in Documentation/virt/kvm/locking.rst.
 
  - if needed, walk the guest page tables to determine the guest translation
    (gva->gpa or ngpa->gpa)
index 1f86a9d3f7057117e30e14add987a5faa8a3f2a0..dc01aea4057b20404ca7253b799f7582eccb7b34 100644 (file)
@@ -10,7 +10,7 @@ Review checklist for kvm patches
 2.  Patches should be against kvm.git master branch.
 
 3.  If the patch introduces or modifies a new userspace API:
-    - the API must be documented in Documentation/virt/kvm/api.txt
+    - the API must be documented in Documentation/virt/kvm/api.rst
     - the API must be discoverable using KVM_CHECK_EXTENSION
 
 4.  New state must include support for save/restore.
index e8d943b21cf925137663f8ebdf8edc187757cba5..611140ffef7eef40ad8e6c8d3c6d8b96afa0a2fa 100644 (file)
@@ -31,6 +31,7 @@ descriptions of data structures and algorithms.
    active_mm
    balance
    cleancache
+   free_page_reporting
    frontswap
    highmem
    hmm
index 637cc49d1b2ffd4c53524138174138adc3ef2a99..7d6f9385d129eb730094b87b18f14f879ab23c77 100644 (file)
@@ -26,7 +26,7 @@ to be disabled when executing the fragment allocation.
 
 The network stack uses two separate caches per CPU to handle fragment
 allocation.  The netdev_alloc_cache is used by callers making use of the
-__netdev_alloc_frag and __netdev_alloc_skb calls.  The napi_alloc_cache is
+netdev_alloc_frag and __netdev_alloc_skb calls.  The napi_alloc_cache is
 used by callers of the __napi_alloc_frag and __napi_alloc_skb calls.  The
 main difference between these two calls is the context in which they may be
 called.  The "netdev" prefixed functions are usable in any context as these
index 933ada4368ff366a20fe98a8f3e03befeb72880b..4eee598555c997ede3ba93d4e6d3af499e2e3025 100644 (file)
@@ -49,7 +49,7 @@ Possible debug options are::
        P               Poisoning (object and padding)
        U               User tracking (free and alloc)
        T               Trace (please only use on single slabs)
-       A               Toggle failslab filter mark for the cache
+       A               Enable failslab filter mark for the cache
        O               Switch debugging off for caches that would have
                        caused higher minimum slab orders
        -               Switch all debugging off (useful if the kernel is
index f8c6a79d7c701e043cb4f2798db9c94aaccf9ec5..d8d9fa4a1f0dfc1e3ba2e70a3dbac772f26ae2df 100644 (file)
@@ -140,10 +140,10 @@ without any real benefit but with a performance drop for the system), a
 special parameter has been introduced to implement a sort of hysteresis to
 refuse taking pages into zswap pool until it has sufficient space if the limit
 has been hit. To set the threshold at which zswap would start accepting pages
-again after it became full, use the sysfs ``accept_threhsold_percent``
+again after it became full, use the sysfs ``accept_threshold_percent``
 attribute, e. g.::
 
-       echo 80 > /sys/module/zswap/parameters/accept_threhsold_percent
+       echo 80 > /sys/module/zswap/parameters/accept_threshold_percent
 
 Setting this parameter to 100 will disable the hysteresis.
 
index dd934cc08e40b35c5b297fb4fe321313a6261164..a1c3f038ce0e7caf7564ac9826c39514bb888bc1 100644 (file)
@@ -2,7 +2,7 @@
 Converting old watchdog drivers to the watchdog framework
 =========================================================
 
-by Wolfram Sang <w.sang@pengutronix.de>
+by Wolfram Sang <wsa@kernel.org>
 
 Before the watchdog framework came into the kernel, every driver had to
 implement the API on its own. Now, as the framework factored out the common
@@ -115,7 +115,7 @@ Add the watchdog operations
 ---------------------------
 
 All possible callbacks are defined in 'struct watchdog_ops'. You can find it
-explained in 'watchdog-kernel-api.txt' in this directory. start(), stop() and
+explained in 'watchdog-kernel-api.txt' in this directory. start() and
 owner must be set, the rest are optional. You will easily find corresponding
 functions in the old driver. Note that you will now get a pointer to the
 watchdog_device as a parameter to these functions, so you probably have to
index 864edbe932c167978c6cc0753a2b95441a8c0dc8..068a55ee0d4a3183518304c629b28f2a9fd307c9 100644 (file)
@@ -123,8 +123,8 @@ The list of watchdog operations is defined as::
        struct module *owner;
        /* mandatory operations */
        int (*start)(struct watchdog_device *);
-       int (*stop)(struct watchdog_device *);
        /* optional operations */
+       int (*stop)(struct watchdog_device *);
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
index 88c3ba32546f4d1b6d19ab8f0a58b277e190775e..3b894103a73460bf833249347b881d3cdad21b0b 100644 (file)
@@ -36,7 +36,7 @@ Mechanics
 
        elilo bootloader with x86_64 support, elilo configuration file,
        kernel image built in first step and corresponding
-       initrd. Instructions on building elilo  and its dependencies
+       initrd. Instructions on building elilo and its dependencies
        can be found in the elilo sourceforge project.
 
 - Boot to EFI shell and invoke elilo choosing the kernel image built
index 50659d76976b712dffa0e14e287f2e142235116a..0e04d5321caf51e320b16edbb7a3d27fd29efccc 100644 (file)
@@ -842,6 +842,13 @@ S: Supported
 T:     git git://people.freedesktop.org/~agd5f/linux
 F:     drivers/gpu/drm/amd/display/
 
+AMD ENERGY DRIVER
+M:     Naveen Krishna Chatradhi <nchatrad@amd.com>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     Documentation/hwmon/amd_energy.rst
+F:     drivers/hwmon/amd_energy.c
+
 AMD FAM15H PROCESSOR POWER MONITORING DRIVER
 M:     Huang Rui <ray.huang@amd.com>
 L:     linux-hwmon@vger.kernel.org
@@ -892,6 +899,11 @@ F: drivers/gpu/drm/amd/include/v9_structs.h
 F:     drivers/gpu/drm/amd/include/vi_structs.h
 F:     include/uapi/linux/kfd_ioctl.h
 
+AMD SPI DRIVER
+M:     Sanjay R Mehta <sanju.mehta@amd.com>
+S:     Maintained
+F:     drivers/spi/spi-amd.c
+
 AMD MP2 I2C DRIVER
 M:     Elie Morisse <syniurge@gmail.com>
 M:     Nehal Shah <nehal-bakulchandra.shah@amd.com>
@@ -2225,6 +2237,7 @@ F:        drivers/*/qcom*
 F:     drivers/*/qcom/
 F:     drivers/bluetooth/btqcomsmd.c
 F:     drivers/clocksource/timer-qcom.c
+F:     drivers/cpuidle/cpuidle-qcom-spm.c
 F:     drivers/extcon/extcon-qcom*
 F:     drivers/i2c/busses/i2c-qcom-geni.c
 F:     drivers/i2c/busses/i2c-qup.c
@@ -3730,7 +3743,7 @@ CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
 M:     David Howells <dhowells@redhat.com>
 L:     linux-cachefs@redhat.com (moderated for non-subscribers)
 S:     Supported
-F:     Documentation/filesystems/caching/cachefiles.txt
+F:     Documentation/filesystems/caching/cachefiles.rst
 F:     fs/cachefiles/
 
 CADENCE MIPI-CSI2 BRIDGES
@@ -3896,6 +3909,15 @@ S:       Supported
 W:     https://developer.arm.com/products/system-ip/trustzone-cryptocell/cryptocell-700-family
 F:     drivers/crypto/ccree/
 
+CCTRNG ARM TRUSTZONE CRYPTOCELL TRUE RANDOM NUMBER GENERATOR (TRNG) DRIVER
+M:     Hadar Gat <hadar.gat@arm.com>
+L:     linux-crypto@vger.kernel.org
+S:     Supported
+F:     drivers/char/hw_random/cctrng.c
+F:     drivers/char/hw_random/cctrng.h
+F:     Documentation/devicetree/bindings/rng/arm-cctrng.txt
+W:     https://developer.arm.com/products/system-ip/trustzone-cryptocell/cryptocell-700-family
+
 CEC FRAMEWORK
 M:     Hans Verkuil <hverkuil-cisco@xs4all.nl>
 L:     linux-media@vger.kernel.org
@@ -4198,7 +4220,7 @@ M:        coda@cs.cmu.edu
 L:     codalist@coda.cs.cmu.edu
 S:     Maintained
 W:     http://www.coda.cs.cmu.edu/
-F:     Documentation/filesystems/coda.txt
+F:     Documentation/filesystems/coda.rst
 F:     fs/coda/
 F:     include/linux/coda*.h
 F:     include/uapi/linux/coda*.h
@@ -4991,7 +5013,7 @@ M:        Jan Kara <jack@suse.cz>
 R:     Amir Goldstein <amir73il@gmail.com>
 L:     linux-fsdevel@vger.kernel.org
 S:     Maintained
-F:     Documentation/filesystems/dnotify.txt
+F:     Documentation/filesystems/dnotify.rst
 F:     fs/notify/dnotify/
 F:     include/linux/dnotify.h
 
@@ -5005,7 +5027,7 @@ W:        http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
 DISKQUOTA
 M:     Jan Kara <jack@suse.com>
 S:     Maintained
-F:     Documentation/filesystems/quota.txt
+F:     Documentation/filesystems/quota.rst
 F:     fs/quota/
 F:     include/linux/quota*.h
 F:     include/uapi/linux/quota*.h
@@ -6172,7 +6194,6 @@ M:        Yash Shah <yash.shah@sifive.com>
 L:     linux-edac@vger.kernel.org
 S:     Supported
 F:     drivers/edac/sifive_edac.c
-F:     drivers/soc/sifive_l2_cache.c
 
 EDAC-SKYLAKE
 M:     Tony Luck <tony.luck@intel.com>
@@ -6713,6 +6734,13 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/crypto/fsl-sec4.txt
 F:     drivers/crypto/caam/
 
+FREESCALE COLDFIRE M5441X MMC DRIVER
+M:     Angelo Dureghello <angelo.dureghello@timesys.com>
+L:     linux-mmc@vger.kernel.org
+S:     Maintained
+F:     drivers/mmc/host/sdhci-esdhc-mcf.c
+F:     include/linux/platform_data/mmc-esdhc-mcf.h
+
 FREESCALE DIU FRAMEBUFFER DRIVER
 M:     Timur Tabi <timur@kernel.org>
 L:     linux-fbdev@vger.kernel.org
@@ -7020,13 +7048,24 @@ R:      Darren Hart <dvhart@infradead.org>
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
-F:     Documentation/*futex*
+F:     Documentation/locking/*futex*
 F:     include/asm-generic/futex.h
 F:     include/linux/futex.h
 F:     include/uapi/linux/futex.h
 F:     kernel/futex.c
 F:     tools/perf/bench/futex*
-F:     tools/testing/selftests/futex/
+F:     Documentation/locking/*futex*
+
+GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER
+M:     Tim Harvey <tharvey@gateworks.com>
+M:     Robert Jones <rjones@gateworks.com>
+S:     Maintained
+F:     Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml
+F:     drivers/mfd/gateworks-gsc.c
+F:     include/linux/mfd/gsc.h
+F:     Documentation/hwmon/gsc-hwmon.rst
+F:     drivers/hwmon/gsc-hwmon.c
+F:     include/linux/platform_data/gsc_hwmon.h
 
 GASKET DRIVER FRAMEWORK
 M:     Rob Springer <rspringer@google.com>
@@ -7496,7 +7535,7 @@ L:        linux-remoteproc@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc.git hwspinlock-next
 F:     Documentation/devicetree/bindings/hwlock/
-F:     Documentation/hwspinlock.txt
+F:     Documentation/locking/hwspinlock.rst
 F:     drivers/hwspinlock/
 F:     include/linux/hwspinlock.h
 
@@ -8491,6 +8530,13 @@ L:       platform-driver-x86@vger.kernel.org
 S:     Maintained
 F:     drivers/platform/x86/intel_atomisp2_pm.c
 
+INTEL BROXTON PMC DRIVER
+M:     Mika Westerberg <mika.westerberg@linux.intel.com>
+M:     Zha Qipeng <qipeng.zha@intel.com>
+S:     Maintained
+F:     drivers/mfd/intel_pmc_bxt.c
+F:     include/linux/mfd/intel_pmc_bxt.h
+
 INTEL C600 SERIES SAS CONTROLLER DRIVER
 M:     Intel SCU Linux support <intel-linux-scu@intel.com>
 M:     Artur Paszkiewicz <artur.paszkiewicz@intel.com>
@@ -8698,6 +8744,13 @@ F:       include/uapi/linux/mic_common.h
 F:     include/uapi/linux/mic_ioctl.h
 F:     include/uapi/linux/scif_ioctl.h
 
+INTEL P-Unit IPC DRIVER
+M:     Zha Qipeng <qipeng.zha@intel.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     arch/x86/include/asm/intel_punit_ipc.h
+F:     drivers/platform/x86/intel_punit_ipc.c
+
 INTEL PMC CORE DRIVER
 M:     Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
 M:     Vishwanath Somayaji <vishwanath.somayaji@intel.com>
@@ -8705,15 +8758,6 @@ L:       platform-driver-x86@vger.kernel.org
 S:     Maintained
 F:     drivers/platform/x86/intel_pmc_core*
 
-INTEL PMC/P-Unit IPC DRIVER
-M:     Zha Qipeng<qipeng.zha@intel.com>
-L:     platform-driver-x86@vger.kernel.org
-S:     Maintained
-F:     arch/x86/include/asm/intel_pmc_ipc.h
-F:     arch/x86/include/asm/intel_punit_ipc.h
-F:     drivers/platform/x86/intel_pmc_ipc.c
-F:     drivers/platform/x86/intel_punit_ipc.c
-
 INTEL PMIC GPIO DRIVERS
 M:     Andy Shevchenko <andy@kernel.org>
 S:     Maintained
@@ -8752,6 +8796,12 @@ S:       Supported
 F:     drivers/infiniband/hw/i40iw/
 F:     include/uapi/rdma/i40iw-abi.h
 
+INTEL SCU DRIVERS
+M:     Mika Westerberg <mika.westerberg@linux.intel.com>
+S:     Maintained
+F:     arch/x86/include/asm/intel_scu_ipc.h
+F:     drivers/platform/x86/intel_scu_*
+
 INTEL SPEED SELECT TECHNOLOGY
 M:     Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
 L:     platform-driver-x86@vger.kernel.org
@@ -8818,6 +8868,13 @@ F:       Documentation/admin-guide/wimax/i2400m.rst
 F:     drivers/net/wimax/i2400m/
 F:     include/uapi/linux/wimax/i2400m.h
 
+INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER
+M:     Jithu Joseph <jithu.joseph@intel.com>
+R:     Maurice Ma <maurice.ma@intel.com>
+S:     Maintained
+W:     https://slimbootloader.github.io/security/firmware-update.html
+F:     drivers/platform/x86/intel-wmi-sbl-fw-update.c
+
 INTEL WMI THUNDERBOLT FORCE POWER DRIVER
 M:     Mario Limonciello <mario.limonciello@dell.com>
 S:     Maintained
@@ -8903,7 +8960,7 @@ M:        Corey Minyard <minyard@acm.org>
 L:     openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
 S:     Supported
 W:     http://openipmi.sourceforge.net/
-F:     Documentation/IPMI.txt
+F:     Documentation/driver-api/ipmi.rst
 F:     Documentation/devicetree/bindings/ipmi/
 F:     drivers/char/ipmi/
 F:     include/linux/ipmi*
@@ -8945,7 +9002,7 @@ IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
 M:     Marc Zyngier <maz@kernel.org>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
-F:     Documentation/IRQ-domain.txt
+F:     Documentation/core-api/irq/irq-domain.rst
 F:     include/linux/irqdomain.h
 F:     kernel/irq/irqdomain.c
 F:     kernel/irq/msi.c
@@ -9725,6 +9782,13 @@ F:       drivers/lightnvm/
 F:     include/linux/lightnvm.h
 F:     include/uapi/linux/lightnvm.h
 
+LINEAR RANGES HELPERS
+M:     Mark Brown <broonie@kernel.org>
+R:     Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
+F:     lib/linear_ranges.c
+F:     lib/test_linear_ranges.c
+F:     include/linux/linear_range.h
+
 LINUX FOR POWER MACINTOSH
 M:     Benjamin Herrenschmidt <benh@kernel.crashing.org>
 L:     linuxppc-dev@lists.ozlabs.org
@@ -13676,6 +13740,7 @@ M:      Tony Luck <tony.luck@intel.com>
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
 F:     Documentation/admin-guide/ramoops.rst
+F:     Documentation/admin-guide/pstore-blk.rst
 F:     Documentation/devicetree/bindings/reserved-memory/ramoops.txt
 F:     drivers/acpi/apei/erst.c
 F:     drivers/firmware/efi/efi-pstore.c
@@ -14230,7 +14295,7 @@ M:      Reinette Chatre <reinette.chatre@intel.com>
 L:     linux-kernel@vger.kernel.org
 S:     Supported
 F:     Documentation/x86/resctrl*
-F:     arch/x86/include/asm/resctrl_sched.h
+F:     arch/x86/include/asm/resctrl.h
 F:     arch/x86/kernel/cpu/resctrl/
 F:     tools/testing/selftests/resctrl/
 
@@ -15479,6 +15544,15 @@ M:     Nicolas Pitre <nico@fluxnic.net>
 S:     Odd Fixes
 F:     drivers/net/ethernet/smsc/smc91x.*
 
+SECURE MONITOR CALL(SMC) CALLING CONVENTION (SMCCC)
+M:     Mark Rutland <mark.rutland@arm.com>
+M:     Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M:     Sudeep Holla <sudeep.holla@arm.com>
+L:     linux-arm-kernel@lists.infradead.org
+S:     Maintained
+F:     drivers/firmware/smccc/
+F:     include/linux/arm-smccc.h
+
 SMIA AND SMIA++ IMAGE SENSOR DRIVER
 M:     Sakari Ailus <sakari.ailus@linux.intel.com>
 L:     linux-media@vger.kernel.org
@@ -15655,7 +15729,7 @@ F:      drivers/ssb/
 F:     include/linux/ssb/
 
 SONY IMX214 SENSOR DRIVER
-M:     Ricardo Ribalda <ricardo.ribalda@gmail.com>
+M:     Ricardo Ribalda <ribalda@kernel.org>
 L:     linux-media@vger.kernel.org
 S:     Maintained
 T:     git git://linuxtv.org/media_tree.git
@@ -15895,7 +15969,7 @@ M:      Jeremy Kerr <jk@ozlabs.org>
 L:     linuxppc-dev@lists.ozlabs.org
 S:     Supported
 W:     http://www.ibm.com/developerworks/power/cell/
-F:     Documentation/filesystems/spufs.txt
+F:     Documentation/filesystems/spufs/spufs.rst
 F:     arch/powerpc/platforms/cell/spufs/
 
 SQUASHFS FILE SYSTEM
@@ -16642,7 +16716,7 @@ S:      Maintained
 F:     sound/soc/ti/
 
 TEXAS INSTRUMENTS' DAC7612 DAC DRIVER
-M:     Ricardo Ribalda <ricardo@ribalda.com>
+M:     Ricardo Ribalda <ribalda@kernel.org>
 L:     linux-iio@vger.kernel.org
 S:     Supported
 F:     Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
@@ -18546,8 +18620,8 @@ W:      http://xfs.org/
 T:     git git://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git
 F:     Documentation/ABI/testing/sysfs-fs-xfs
 F:     Documentation/admin-guide/xfs.rst
-F:     Documentation/filesystems/xfs-delayed-logging-design.txt
-F:     Documentation/filesystems/xfs-self-describing-metadata.txt
+F:     Documentation/filesystems/xfs-delayed-logging-design.rst
+F:     Documentation/filesystems/xfs-self-describing-metadata.rst
 F:     fs/xfs/
 F:     include/uapi/linux/dqblk_xfs.h
 F:     include/uapi/linux/fsmap.h
index f0d118b86287832467015f0840ff283de94423cc..a7bc91cbac8fcb1f306185fb08362693508ef44d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 7
 SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
 NAME = Kleptomaniac Octopus
 
 # *DOCUMENTATION*
@@ -862,6 +862,12 @@ ifdef CONFIG_LIVEPATCH
 KBUILD_CFLAGS += $(call cc-option, -flive-patching=inline-clone)
 endif
 
+ifdef CONFIG_SHADOW_CALL_STACK
+CC_FLAGS_SCS   := -fsanitize=shadow-call-stack
+KBUILD_CFLAGS  += $(CC_FLAGS_SCS)
+export CC_FLAGS_SCS
+endif
+
 # arch Makefile may override CC so keep this after arch Makefile is included
 NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
 
index 786a85d4ad40d2ce9b991dc4b16ee64dbe5ff559..2e6f843d87c47d7cc86cab234dc5858d3d79f5ca 100644 (file)
@@ -533,6 +533,31 @@ config STACKPROTECTOR_STRONG
          about 20% of all kernel functions, which increases the kernel code
          size by about 2%.
 
+config ARCH_SUPPORTS_SHADOW_CALL_STACK
+       bool
+       help
+         An architecture should select this if it supports Clang's Shadow
+         Call Stack and implements runtime support for shadow stack
+         switching.
+
+config SHADOW_CALL_STACK
+       bool "Clang Shadow Call Stack"
+       depends on CC_IS_CLANG && ARCH_SUPPORTS_SHADOW_CALL_STACK
+       depends on DYNAMIC_FTRACE_WITH_REGS || !FUNCTION_GRAPH_TRACER
+       help
+         This option enables Clang's Shadow Call Stack, which uses a
+         shadow stack to protect function return addresses from being
+         overwritten by an attacker. More information can be found in
+         Clang's documentation:
+
+           https://clang.llvm.org/docs/ShadowCallStack.html
+
+         Note that security guarantees in the kernel differ from the
+         ones documented for user space. The kernel must store addresses
+         of shadow stacks in memory, which means an attacker capable of
+         reading and writing arbitrary memory may be able to locate them
+         and hijack control flow by modifying the stacks.
+
 config HAVE_ARCH_WITHIN_STACK_FRAMES
        bool
        help
index 473e6ccb65a3949daf6efcedffaf83dade12dfdb..0eac81624d01c2ebdd45c282892689b676339431 100644 (file)
@@ -41,7 +41,8 @@ extern __wsum csum_partial(const void *buff, int len, __wsum sum);
  * here even more important to align src and dst on a 32-bit (or even
  * better 64-bit) boundary
  */
-__wsum csum_partial_copy_from_user(const void __user *src, void *dst, int len, __wsum sum, int *errp);
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
+__wsum csum_and_copy_from_user(const void __user *src, void *dst, int len, __wsum sum, int *errp);
 
 __wsum csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum);
 
index 36d42da7466aae5ccd8a8a70d0e70014ae63c2df..5ddd128d4b7ac0c6cdbf65dbc4bacb993c2a5413 100644 (file)
 # 545 reserved for clone3
 547    common  openat2                         sys_openat2
 548    common  pidfd_getfd                     sys_pidfd_getfd
+549    common  faccessat2                      sys_faccessat2
index e53f96e8aa6d2bbe60bed40d0d549f3b428cdbc5..af1dad74e9333ecf8992e56ce7cd2115664ebc16 100644 (file)
@@ -325,7 +325,7 @@ csum_partial_cfu_unaligned(const unsigned long __user * src,
 }
 
 __wsum
-csum_partial_copy_from_user(const void __user *src, void *dst, int len,
+csum_and_copy_from_user(const void __user *src, void *dst, int len,
                               __wsum sum, int *errp)
 {
        unsigned long checksum = (__force u32) sum;
@@ -369,7 +369,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
        }
        return (__force __wsum)checksum;
 }
-EXPORT_SYMBOL(csum_partial_copy_from_user);
+EXPORT_SYMBOL(csum_and_copy_from_user);
 
 __wsum
 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
@@ -377,7 +377,7 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
        __wsum checksum;
        mm_segment_t oldfs = get_fs();
        set_fs(KERNEL_DS);
-       checksum = csum_partial_copy_from_user((__force const void __user *)src,
+       checksum = csum_and_copy_from_user((__force const void __user *)src,
                                                dst, len, sum, NULL);
        set_fs(oldfs);
        return checksum;
index c77c93c485a081745acdd98c39d693ffc6eae3a6..16fbf74030fe2d72566babd6f68143c5bca85ec5 100644 (file)
@@ -313,6 +313,9 @@ choice
 config ARCH_MULTIPLATFORM
        bool "Allow multiple platforms to be selected"
        depends on MMU
+       select ARCH_FLATMEM_ENABLE
+       select ARCH_SPARSEMEM_ENABLE
+       select ARCH_SELECT_MEMORY_MODEL
        select ARM_HAS_SG_CHAIN
        select ARM_PATCH_PHYS_VIRT
        select AUTO_ZRELADDR
@@ -1516,11 +1519,15 @@ config OABI_COMPAT
 config ARCH_HAS_HOLES_MEMORYMODEL
        bool
 
-config ARCH_SPARSEMEM_ENABLE
+config ARCH_SELECT_MEMORY_MODEL
+       bool
+
+config ARCH_FLATMEM_ENABLE
        bool
 
-config ARCH_SPARSEMEM_DEFAULT
-       def_bool ARCH_SPARSEMEM_ENABLE
+config ARCH_SPARSEMEM_ENABLE
+       bool
+       select SPARSEMEM_STATIC if SPARSEMEM
 
 config HAVE_ARCH_PFN_VALID
        def_bool ARCH_HAS_HOLES_MEMORYMODEL || !SPARSEMEM
@@ -1955,7 +1962,7 @@ config EFI
        select UCS2_STRING
        select EFI_PARAMS_FROM_FDT
        select EFI_STUB
-       select EFI_ARMSTUB
+       select EFI_GENERIC_STUB
        select EFI_RUNTIME_WRAPPERS
        ---help---
          This option provides support for runtime services provided
index db05c6ef3e31325521475447818ddca0d8f35341..60606b0f378d59e80880e9a3ad01ff6f53ed5174 100644 (file)
@@ -7,12 +7,3 @@ hyp-stub.S
 piggy_data
 vmlinux
 vmlinux.lds
-
-# borrowed libfdt files
-fdt.c
-fdt.h
-fdt_ro.c
-fdt_rw.c
-fdt_wip.c
-libfdt.h
-libfdt_internal.h
index 9c11e7490292f0e0031b2dfb4f7e0386b3bcee38..00602a6fba04733ffb9aa64dbf0022e5469db8b9 100644 (file)
@@ -76,29 +76,30 @@ compress-$(CONFIG_KERNEL_LZMA) = lzma
 compress-$(CONFIG_KERNEL_XZ)   = xzkern
 compress-$(CONFIG_KERNEL_LZ4)  = lz4
 
-# Borrowed libfdt files for the ATAG compatibility mode
-
-libfdt         := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c
-libfdt_hdrs    := fdt.h libfdt.h libfdt_internal.h
-
-libfdt_objs    := $(addsuffix .o, $(basename $(libfdt)))
-
-$(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/libfdt/%
-       $(call cmd,shipped)
-
-$(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
-       $(addprefix $(obj)/,$(libfdt_hdrs))
+libfdt_objs := fdt_rw.o fdt_ro.o fdt_wip.o fdt.o
 
 ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
 OBJS   += $(libfdt_objs) atags_to_fdt.o
 endif
 
+# -fstack-protector-strong triggers protection checks in this code,
+# but it is being used too early to link to meaningful stack_chk logic.
+nossp-flags-$(CONFIG_CC_HAS_STACKPROTECTOR_NONE) := -fno-stack-protector
+$(foreach o, $(libfdt_objs) atags_to_fdt.o, \
+       $(eval CFLAGS_$(o) := -I $(srctree)/scripts/dtc/libfdt $(nossp-flags-y)))
+
+# These were previously generated C files. When you are building the kernel
+# with O=, make sure to remove the stale files in the output tree. Otherwise,
+# the build system wrongly compiles the stale ones.
+ifdef building_out_of_srctree
+$(shell rm -f $(addprefix $(obj)/, fdt_rw.c fdt_ro.c fdt_wip.c fdt.c))
+endif
+
 targets       := vmlinux vmlinux.lds piggy_data piggy.o \
                 lib1funcs.o ashldi3.o bswapsdi2.o \
                 head.o $(OBJS)
 
-clean-files += piggy_data lib1funcs.S ashldi3.S bswapsdi2.S \
-               $(libfdt) $(libfdt_hdrs) hyp-stub.S
+clean-files += piggy_data lib1funcs.S ashldi3.S bswapsdi2.S hyp-stub.S
 
 KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
 
@@ -107,15 +108,6 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
 KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
 endif
 
-# -fstack-protector-strong triggers protection checks in this code,
-# but it is being used too early to link to meaningful stack_chk logic.
-nossp-flags-$(CONFIG_CC_HAS_STACKPROTECTOR_NONE) := -fno-stack-protector
-CFLAGS_atags_to_fdt.o := $(nossp-flags-y)
-CFLAGS_fdt.o := $(nossp-flags-y)
-CFLAGS_fdt_ro.o := $(nossp-flags-y)
-CFLAGS_fdt_rw.o := $(nossp-flags-y)
-CFLAGS_fdt_wip.o := $(nossp-flags-y)
-
 ccflags-y := -fpic $(call cc-option,-mno-single-pic-base,) -fno-builtin \
             -I$(obj) $(DISABLE_ARM_SSP_PER_TASK_PLUGIN)
 asflags-y := -DZIMAGE
index 64c49747f8a3108f6638e300f16df19b7be68d12..8452753efebe5621e2569db95df2b4c30b26df99 100644 (file)
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/libfdt_env.h>
 #include <asm/setup.h>
 #include <libfdt.h>
 
index 62286da318e7da5d4da1d8c22b73c57c0665a453..c0e7a745103e22b8618618e971ba3ab41b95e5af 100644 (file)
@@ -60,7 +60,7 @@ optional_header:
                .long   __pecoff_code_size              @ SizeOfCode
                .long   __pecoff_data_size              @ SizeOfInitializedData
                .long   0                               @ SizeOfUninitializedData
-               .long   efi_entry - start               @ AddressOfEntryPoint
+               .long   efi_pe_entry - start            @ AddressOfEntryPoint
                .long   start_offset                    @ BaseOfCode
                .long   __pecoff_data_start - start     @ BaseOfData
 
diff --git a/arch/arm/boot/compressed/fdt.c b/arch/arm/boot/compressed/fdt.c
new file mode 100644 (file)
index 0000000..f8ea7a2
--- /dev/null
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../../../lib/fdt.c"
diff --git a/arch/arm/boot/compressed/fdt_ro.c b/arch/arm/boot/compressed/fdt_ro.c
new file mode 100644 (file)
index 0000000..93970a4
--- /dev/null
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../../../lib/fdt_ro.c"
diff --git a/arch/arm/boot/compressed/fdt_rw.c b/arch/arm/boot/compressed/fdt_rw.c
new file mode 100644 (file)
index 0000000..f7c6b8b
--- /dev/null
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../../../lib/fdt_rw.c"
diff --git a/arch/arm/boot/compressed/fdt_wip.c b/arch/arm/boot/compressed/fdt_wip.c
new file mode 100644 (file)
index 0000000..048d2c7
--- /dev/null
@@ -0,0 +1,2 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "../../../../lib/fdt_wip.c"
index e8e1c866e413a287924656851a202f1c480613b7..c79db44ba1284bdcdbcfb188d301ef5144e4c43c 100644 (file)
@@ -287,28 +287,22 @@ not_angel:
                 */
                mov     r0, pc
                cmp     r0, r4
-               ldrcc   r0, LC0+28
+               ldrcc   r0, .Lheadroom
                addcc   r0, r0, pc
                cmpcc   r4, r0
                orrcc   r4, r4, #1              @ remember we skipped cache_on
                blcs    cache_on
 
-restart:       adr     r0, LC0
-               ldmia   r0, {r1, r2, r3, r6, r11, r12}
-               ldr     sp, [r0, #24]
-
-               /*
-                * We might be running at a different address.  We need
-                * to fix up various pointers.
-                */
-               sub     r0, r0, r1              @ calculate the delta offset
-               add     r6, r6, r0              @ _edata
+restart:       adr     r0, LC1
+               ldr     sp, [r0]
+               ldr     r6, [r0, #4]
+               add     sp, sp, r0
+               add     r6, r6, r0
 
                get_inflated_image_size r9, r10, lr
 
 #ifndef CONFIG_ZBOOT_ROM
                /* malloc space is above the relocated stack (64k max) */
-               add     sp, sp, r0
                add     r10, sp, #0x10000
 #else
                /*
@@ -322,9 +316,6 @@ restart:    adr     r0, LC0
                mov     r5, #0                  @ init dtb size to 0
 #ifdef CONFIG_ARM_APPENDED_DTB
 /*
- *   r0  = delta
- *   r2  = BSS start
- *   r3  = BSS end
  *   r4  = final kernel address (possibly with LSB set)
  *   r5  = appended dtb size (still unknown)
  *   r6  = _edata
@@ -332,8 +323,6 @@ restart:    adr     r0, LC0
  *   r8  = atags/device tree pointer
  *   r9  = size of decompressed image
  *   r10 = end of this image, including  bss/stack/malloc space if non XIP
- *   r11 = GOT start
- *   r12 = GOT end
  *   sp  = stack pointer
  *
  * if there are device trees (dtb) appended to zImage, advance r10 so that the
@@ -381,7 +370,6 @@ restart:    adr     r0, LC0
                /* temporarily relocate the stack past the DTB work space */
                add     sp, sp, r5
 
-               stmfd   sp!, {r0-r3, ip, lr}
                mov     r0, r8
                mov     r1, r6
                mov     r2, r5
@@ -400,7 +388,6 @@ restart:    adr     r0, LC0
                mov     r2, r5
                bleq    atags_to_fdt
 
-               ldmfd   sp!, {r0-r3, ip, lr}
                sub     sp, sp, r5
 #endif
 
@@ -537,6 +524,10 @@ dtb_check_done:
                mov     pc, r0
 
 wont_overwrite:
+               adr     r0, LC0
+               ldmia   r0, {r1, r2, r3, r11, r12}
+               sub     r0, r0, r1              @ calculate the delta offset
+
 /*
  * If delta is zero, we are running at the address we were linked at.
  *   r0  = delta
@@ -660,13 +651,18 @@ not_relocated:    mov     r0, #0
 LC0:           .word   LC0                     @ r1
                .word   __bss_start             @ r2
                .word   _end                    @ r3
-               .word   _edata                  @ r6
                .word   _got_start              @ r11
                .word   _got_end                @ ip
-               .word   .L_user_stack_end       @ sp
-               .word   _end - restart + 16384 + 1024*1024
                .size   LC0, . - LC0
 
+               .type   LC1, #object
+LC1:           .word   .L_user_stack_end - LC1 @ sp
+               .word   _edata - LC1            @ r6
+               .size   LC1, . - LC1
+
+.Lheadroom:
+               .word   _end - restart + 16384 + 1024*1024
+
 .Linflated_image_size_offset:
                .long   (input_data_end - 4) - .
 
@@ -1434,38 +1430,26 @@ reloc_code_end:
 
 #ifdef CONFIG_EFI_STUB
 ENTRY(efi_enter_kernel)
-               mov     r7, r0                          @ preserve image base
-               mov     r4, r1                          @ preserve DT pointer
+               mov     r4, r0                  @ preserve image base
+               mov     r8, r1                  @ preserve DT pointer
 
-               mov     r0, r4                          @ DT start
-               add     r1, r4, r2                      @ DT end
-               bl      cache_clean_flush
+               mrc     p15, 0, r0, c1, c0, 0   @ read SCTLR
+               tst     r0, #0x1                @ MMU enabled?
+               orreq   r4, r4, #1              @ set LSB if not
 
-               mov     r0, r7                          @ relocated zImage
-               ldr     r1, =_edata                     @ size of zImage
-               add     r1, r1, r0                      @ end of zImage
+               mov     r0, r8                  @ DT start
+               add     r1, r8, r2              @ DT end
                bl      cache_clean_flush
 
-               @ The PE/COFF loader might not have cleaned the code we are
-               @ running beyond the PoU, and so calling cache_off below from
-               @ inside the PE/COFF loader allocated region is unsafe unless
-               @ we explicitly clean it to the PoC.
- ARM(          adrl    r0, call_cache_fn       )
- THUMB(                adr     r0, call_cache_fn       )       @ region of code we will
-               adr     r1, 0f                          @ run with MMU off
-               bl      cache_clean_flush
-               bl      cache_off
+               adr     r0, 0f                  @ switch to our stack
+               ldr     sp, [r0]
+               add     sp, sp, r0
 
-               @ Set parameters for booting zImage according to boot protocol
-               @ put FDT address in r2, it was returned by efi_entry()
-               @ r1 is the machine type, and r0 needs to be 0
-               mov     r0, #0
-               mov     r1, #0xFFFFFFFF
-               mov     r2, r4
-               add     r7, r7, #(__efi_start - start)
-               mov     pc, r7                          @ no mode switch
+               mov     r5, #0                  @ appended DTB size
+               mov     r7, #0xFFFFFFFF         @ machine ID
+               b       wont_overwrite
 ENDPROC(efi_enter_kernel)
-0:
+0:             .long   .L_user_stack_end - .
 #endif
 
                .align
diff --git a/arch/arm/boot/compressed/libfdt_env.h b/arch/arm/boot/compressed/libfdt_env.h
deleted file mode 100644 (file)
index 6a0f1f5..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ARM_LIBFDT_ENV_H
-#define _ARM_LIBFDT_ENV_H
-
-#include <linux/limits.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <asm/byteorder.h>
-
-#define INT32_MAX      S32_MAX
-#define UINT32_MAX     U32_MAX
-
-typedef __be16 fdt16_t;
-typedef __be32 fdt32_t;
-typedef __be64 fdt64_t;
-
-#define fdt16_to_cpu(x)                be16_to_cpu(x)
-#define cpu_to_fdt16(x)                cpu_to_be16(x)
-#define fdt32_to_cpu(x)                be32_to_cpu(x)
-#define cpu_to_fdt32(x)                cpu_to_be32(x)
-#define fdt64_to_cpu(x)                be64_to_cpu(x)
-#define cpu_to_fdt64(x)                cpu_to_be64(x)
-
-#endif
index b247f399de711b36a98f9945d51231797e248659..09ac33f52814a7a4f380118d6bb855c56358a3ce 100644 (file)
@@ -42,7 +42,7 @@ SECTIONS
   }
   .table : ALIGN(4) {
     _table_start = .;
-    LONG(ZIMAGE_MAGIC(2))
+    LONG(ZIMAGE_MAGIC(4))
     LONG(ZIMAGE_MAGIC(0x5a534c4b))
     LONG(ZIMAGE_MAGIC(__piggy_size_addr - _start))
     LONG(ZIMAGE_MAGIC(_kernel_bss_size))
@@ -63,9 +63,11 @@ SECTIONS
   _etext = .;
 
   .got.plt             : { *(.got.plt) }
+#ifndef CONFIG_EFI_STUB
   _got_start = .;
   .got                 : { *(.got) }
   _got_end = .;
+#endif
 
   /* ensure the zImage file size is always a multiple of 64 bits */
   /* (without a dummy byte, ld just ignores the empty section) */
@@ -74,11 +76,14 @@ SECTIONS
 #ifdef CONFIG_EFI_STUB
   .data : ALIGN(4096) {
     __pecoff_data_start = .;
+    _got_start = .;
+    *(.got)
+    _got_end = .;
     /*
      * The EFI stub always executes from RAM, and runs strictly before the
      * decompressor, so we can make an exception for its r/w data, and keep it
      */
-    *(.data.efistub)
+    *(.data.efistub .bss.efistub)
     __pecoff_data_end = .;
 
     /*
index 811c8cae315b520f445964fc07dc00e16266b5f9..d692e3b2812a0e74b1014dec8487c28fd5c0f390 100644 (file)
 
 &cpsw_emac0 {
        phy-handle = <&ethphy0>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
 };
 
 &elm {
index 9f66f96d09c91661a0917e4abcdf701086059ba4..a958f9ee4a5ab9e7b957b19cd10d1984a81594e2 100644 (file)
 
 &cpsw_emac0 {
        phy-handle = <&ethphy0>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
 };
 
 &rtc {
index 25222497f82854a9edbe213c144d3a9f4daa9634..4d5a7ca2e25d4d52955fd52c8f5146909011d8e9 100644 (file)
 
 &cpsw_emac0 {
        phy-handle = <&ethphy0>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <1>;
 };
 
 &cpsw_emac1 {
        phy-handle = <&ethphy1>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <2>;
 };
 
index 669559c9c95b3a83c93e412d7f526cbc6131077c..c13756fa0f55a6dd345c7a5ca6aeea172aafddbc 100644 (file)
 
 &cpsw_port1 {
        phy-handle = <&ethphy0_sw>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        ti,dual-emac-pvid = <1>;
 };
 
 &cpsw_port2 {
        phy-handle = <&ethphy1_sw>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        ti,dual-emac-pvid = <2>;
 };
 
index a813a0cf3ff39a97af53723a5973fe721a1c0117..565675354de429984ce6b77ae4475d42d9c53b88 100644 (file)
 
 &cpsw_emac0 {
        phy-handle = <&phy0>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <1>;
 };
 
 &cpsw_emac1 {
        phy-handle = <&phy1>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <2>;
 };
 
index aa5e55f981792885292dcc28cdce6f29fd3c22ea..a3ff1237d1fac2a27db9c51bc783834043153ff7 100644 (file)
 
 &cpsw_emac0 {
        phy-handle = <&ethphy0>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <1>;
 };
 
 &cpsw_emac1 {
        phy-handle = <&ethphy1>;
-       phy-mode = "rgmii";
+       phy-mode = "rgmii-rxid";
        dual_emac_res_vlan = <2>;
 };
 
index 6142c672811e510f416d3c0cfca2b417d3c03c25..5e5f5ca3c86f1654e457b0608faf0b3985b7babc 100644 (file)
@@ -75,7 +75,7 @@
                timer@20200 {
                        compatible = "arm,cortex-a9-global-timer";
                        reg = <0x20200 0x100>;
-                       interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupts = <GIC_PPI 11 IRQ_TYPE_EDGE_RISING>;
                        clocks = <&periph_clk>;
                };
 
@@ -83,7 +83,7 @@
                        compatible = "arm,cortex-a9-twd-timer";
                        reg = <0x20600 0x20>;
                        interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(1) |
-                                                 IRQ_TYPE_LEVEL_HIGH)>;
+                                                 IRQ_TYPE_EDGE_RISING)>;
                        clocks = <&periph_clk>;
                };
 
@@ -91,7 +91,7 @@
                        compatible = "arm,cortex-a9-twd-wdt";
                        reg = <0x20620 0x20>;
                        interrupts = <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(1) |
-                                                 IRQ_TYPE_LEVEL_HIGH)>;
+                                                 IRQ_TYPE_EDGE_RISING)>;
                        clocks = <&periph_clk>;
                };
 
index 4c3f606e5b8d8ebae250daef6d3a26c0f8488ff3..f65448c01e317ba00ad30dfcfd8137e7dc1cb798 100644 (file)
@@ -24,7 +24,7 @@
 
        leds {
                act {
-                       gpios = <&gpio 47 GPIO_ACTIVE_HIGH>;
+                       gpios = <&gpio 47 GPIO_ACTIVE_LOW>;
                };
        };
 
index 44ed5a79816453e81a47eeacdc13ec7b80bd666d..c28ca0540f034eba6972708bfe8ba0f317b76860 100644 (file)
 
                                        davinci_mdio: mdio@800 {
                                                compatible = "ti,cpsw-mdio", "ti,davinci_mdio";
-                                               clocks = <&alwon_ethernet_clkctrl DM814_ETHERNET_CPGMAC0_CLKCTRL 0>;
+                                               clocks = <&cpsw_125mhz_gclk>;
                                                clock-names = "fck";
                                                #address-cells = <1>;
                                                #size-cells = <0>;
index 95b8f2d7182144ba90d6fa0cce12712e42add476..fb0980190aa07e84499180e99d42580b9ca0805a 100644 (file)
        };
 };
 
-&clks {
-       assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
-                         <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
-       assigned-clock-parents = <&clks IMX6QDL_CLK_PLL3_USB_OTG>,
-                                <&clks IMX6QDL_CLK_PLL3_USB_OTG>;
-};
-
 &ldb {
        status = "okay";
 
index 611cb7ae7e556ec761af31bd7739c589d0405eac..8f762d9c5ae9962f68cce12c5b75938eca1716c4 100644 (file)
        };
 };
 
-&clks {
-       assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
-                         <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
-       assigned-clock-parents = <&clks IMX6QDL_CLK_PLL3_USB_OTG>,
-                                <&clks IMX6QDL_CLK_PLL3_USB_OTG>;
-};
-
 &ldb {
        status = "okay";
 
index e4cb118f88c6c4c7854a176be6ba9dc929ff3d79..1ea64ecf4291c4d661a4ed22b963a351de10585b 100644 (file)
        };
 };
 
-&clks {
-       assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
-                         <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
-                         <&clks IMX6QDL_CLK_IPU1_DI0_PRE_SEL>,
-                         <&clks IMX6QDL_CLK_IPU2_DI0_PRE_SEL>;
-       assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
-                                <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
-                                <&clks IMX6QDL_CLK_PLL2_PFD2_396M>,
-                                <&clks IMX6QDL_CLK_PLL2_PFD2_396M>;
-};
-
 &ldb {
        fsl,dual-channel;
        status = "okay";
index fa27dcdf06f1b3284f6d19812f48ee6f7cd4087b..1938b04199c484892153b9f0d7705935e5464bb1 100644 (file)
                #interrupt-cells = <1>;
        };
 };
+
+&clks {
+       assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
+                         <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
+                         <&clks IMX6QDL_CLK_IPU1_DI0_PRE_SEL>,
+                         <&clks IMX6QDL_CLK_IPU1_DI1_PRE_SEL>,
+                         <&clks IMX6QDL_CLK_IPU2_DI0_PRE_SEL>,
+                         <&clks IMX6QDL_CLK_IPU2_DI1_PRE_SEL>;
+       assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
+                                <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
+                                <&clks IMX6QDL_CLK_PLL2_PFD0_352M>,
+                                <&clks IMX6QDL_CLK_PLL2_PFD0_352M>,
+                                <&clks IMX6QDL_CLK_PLL2_PFD0_352M>,
+                                <&clks IMX6QDL_CLK_PLL2_PFD0_352M>;
+};
index 15449c72c042b9e505cb54526f315dd392eb051f..b0ec14c421641aa0889a26ba6c3a5a303274a322 100644 (file)
        status = "okay";
 };
 
-&ssp3 {
+&ssp1 {
        status = "okay";
-       cs-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
+       cs-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
 
        firmware-flash@0 {
-               compatible = "st,m25p80", "jedec,spi-nor";
+               compatible = "winbond,w25q32", "jedec,spi-nor";
                reg = <0>;
-               spi-max-frequency = <40000000>;
+               spi-max-frequency = <104000000>;
                m25p,fast-read;
        };
 };
 
-&ssp4 {
-       cs-gpios = <&gpio 56 GPIO_ACTIVE_HIGH>;
+&ssp2 {
+       cs-gpios = <&gpio 56 GPIO_ACTIVE_LOW>;
        status = "okay";
 };
index 9b5087a95e736861badbfc6d59829492243f0455..826f0a5778598776d1b8e9abb2f7c31ee3592a70 100644 (file)
                        };
 
                        hsic_phy0: hsic-phy@f0001800 {
-                               compatible = "marvell,mmp3-hsic-phy",
-                                            "usb-nop-xceiv";
+                               compatible = "marvell,mmp3-hsic-phy";
                                reg = <0xf0001800 0x40>;
                                #phy-cells = <0>;
                                status = "disabled";
                        };
 
                        hsic_phy1: hsic-phy@f0002800 {
-                               compatible = "marvell,mmp3-hsic-phy",
-                                            "usb-nop-xceiv";
+                               compatible = "marvell,mmp3-hsic-phy";
                                reg = <0xf0002800 0x40>;
                                #phy-cells = <0>;
                                status = "disabled";
                };
 
                soc_clocks: clocks@d4050000 {
-                       compatible = "marvell,mmp2-clock";
+                       compatible = "marvell,mmp3-clock";
                        reg = <0xd4050000 0x1000>,
                              <0xd4282800 0x400>,
                              <0xd4015000 0x1000>;
index 8b83d4a5d309c6a934a5f471638b0c909c690b2f..fe383f5a92fba84818bdd980e37c983400e739d4 100644 (file)
@@ -81,7 +81,7 @@ CONFIG_PARTITION_ADVANCED=y
 CONFIG_BINFMT_MISC=y
 CONFIG_CMA=y
 CONFIG_ZSMALLOC=m
-CONFIG_PGTABLE_MAPPING=y
+CONFIG_ZSMALLOC_PGTABLE_MAPPING=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
index c80b0ebfd02ff65cbf10ca3b3d504b0aa21231b3..4e954b3f7ecd5d33749e84746951ec3cf04a1c54 100644 (file)
@@ -14,7 +14,6 @@
 #include <crypto/internal/hash.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha1_base.h>
index 2c3627334335df111c3da38f809aa6bc87ff5cf2..0071e5e4411a241ed314008222e2105188c77ee8 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha1_base.h>
index 215497f011f2393461d3e297d78c7fd2ef2ad70f..b8a4f79020cf8a14ae49c05dc241d1eceb55990a 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <crypto/sha.h>
index 38645e415196e6559eacf4426ccf8e261e9402e7..79820b9e2541de89cd19f79731d47783e42d383f 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <crypto/internal/hash.h>
 #include <crypto/internal/simd.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <crypto/sha.h>
index 99929122dad7529b5be4daeb8321dbf7b555c79a..feac2c8b86f29dbe3ad1a8cca83881916865375c 100644 (file)
 #endif
 
 #include <asm/ptrace.h>
-#include <asm/domain.h>
 #include <asm/opcodes-virt.h>
 #include <asm/asm-offsets.h>
 #include <asm/page.h>
 #include <asm/thread_info.h>
+#include <asm/uaccess-asm.h>
 
 #define IOMEM(x)       (x)
 
        .endif                                                  ;\
        .popsection
 #define ALT_UP_B(label)                                        \
-       .equ    up_b_offset, label - 9998b                      ;\
        .pushsection ".alt.smp.init", "a"                       ;\
        .long   9998b                                           ;\
-       W(b)    . + up_b_offset                                 ;\
+       W(b)    . + (label - 9998b)                                     ;\
        .popsection
 #else
 #define ALT_SMP(instr...)
@@ -446,79 +445,6 @@ THUMB(     orr     \reg , \reg , #PSR_T_BIT        )
        .size \name , . - \name
        .endm
 
-       .macro  csdb
-#ifdef CONFIG_THUMB2_KERNEL
-       .inst.w 0xf3af8014
-#else
-       .inst   0xe320f014
-#endif
-       .endm
-
-       .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req
-#ifndef CONFIG_CPU_USE_DOMAINS
-       adds    \tmp, \addr, #\size - 1
-       sbcscc  \tmp, \tmp, \limit
-       bcs     \bad
-#ifdef CONFIG_CPU_SPECTRE
-       movcs   \addr, #0
-       csdb
-#endif
-#endif
-       .endm
-
-       .macro uaccess_mask_range_ptr, addr:req, size:req, limit:req, tmp:req
-#ifdef CONFIG_CPU_SPECTRE
-       sub     \tmp, \limit, #1
-       subs    \tmp, \tmp, \addr       @ tmp = limit - 1 - addr
-       addhs   \tmp, \tmp, #1          @ if (tmp >= 0) {
-       subshs  \tmp, \tmp, \size       @ tmp = limit - (addr + size) }
-       movlo   \addr, #0               @ if (tmp < 0) addr = NULL
-       csdb
-#endif
-       .endm
-
-       .macro  uaccess_disable, tmp, isb=1
-#ifdef CONFIG_CPU_SW_DOMAIN_PAN
-       /*
-        * Whenever we re-enter userspace, the domains should always be
-        * set appropriately.
-        */
-       mov     \tmp, #DACR_UACCESS_DISABLE
-       mcr     p15, 0, \tmp, c3, c0, 0         @ Set domain register
-       .if     \isb
-       instr_sync
-       .endif
-#endif
-       .endm
-
-       .macro  uaccess_enable, tmp, isb=1
-#ifdef CONFIG_CPU_SW_DOMAIN_PAN
-       /*
-        * Whenever we re-enter userspace, the domains should always be
-        * set appropriately.
-        */
-       mov     \tmp, #DACR_UACCESS_ENABLE
-       mcr     p15, 0, \tmp, c3, c0, 0
-       .if     \isb
-       instr_sync
-       .endif
-#endif
-       .endm
-
-       .macro  uaccess_save, tmp
-#ifdef CONFIG_CPU_SW_DOMAIN_PAN
-       mrc     p15, 0, \tmp, c3, c0, 0
-       str     \tmp, [sp, #SVC_DACR]
-#endif
-       .endm
-
-       .macro  uaccess_restore
-#ifdef CONFIG_CPU_SW_DOMAIN_PAN
-       ldr     r0, [sp, #SVC_DACR]
-       mcr     p15, 0, r0, c3, c0, 0
-#endif
-       .endm
-
        .irp    c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
        .macro  ret\c, reg
 #if __LINUX_ARM_ARCH__ < 6
index 20043e0ebb07485084b822fcf92dd3c3fac62500..ed6073fee338aa67fbdf1a6a153d595a842dade8 100644 (file)
@@ -40,6 +40,20 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum);
 __wsum
 csum_partial_copy_from_user(const void __user *src, void *dst, int len, __wsum sum, int *err_ptr);
 
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
+static inline
+__wsum csum_and_copy_from_user (const void __user *src, void *dst,
+                                     int len, __wsum sum, int *err_ptr)
+{
+       if (access_ok(src, len))
+               return csum_partial_copy_from_user(src, dst, len, sum, err_ptr);
+
+       if (len)
+               *err_ptr = -EFAULT;
+
+       return sum;
+}
+
 /*
  *     Fold a partial checksum without adding pseudo headers
  */
index 5ac46e2860bcb2df9bfddd1ede00d9db88110a15..9383f236e7958fc60028fe9125ab87ca9daff0ee 100644 (file)
@@ -50,14 +50,6 @@ void efi_virtmap_unload(void);
 
 /* arch specific definitions used by the stub code */
 
-#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__)
-#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__)
-#define efi_is_native()                (true)
-
-#define efi_table_attr(inst, attr)     (inst->attr)
-
-#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
-
 struct screen_info *alloc_screen_info(void);
 void free_screen_info(struct screen_info *si);
 
diff --git a/arch/arm/include/asm/uaccess-asm.h b/arch/arm/include/asm/uaccess-asm.h
new file mode 100644 (file)
index 0000000..907571f
--- /dev/null
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __ASM_UACCESS_ASM_H__
+#define __ASM_UACCESS_ASM_H__
+
+#include <asm/asm-offsets.h>
+#include <asm/domain.h>
+#include <asm/memory.h>
+#include <asm/thread_info.h>
+
+       .macro  csdb
+#ifdef CONFIG_THUMB2_KERNEL
+       .inst.w 0xf3af8014
+#else
+       .inst   0xe320f014
+#endif
+       .endm
+
+       .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req
+#ifndef CONFIG_CPU_USE_DOMAINS
+       adds    \tmp, \addr, #\size - 1
+       sbcscc  \tmp, \tmp, \limit
+       bcs     \bad
+#ifdef CONFIG_CPU_SPECTRE
+       movcs   \addr, #0
+       csdb
+#endif
+#endif
+       .endm
+
+       .macro uaccess_mask_range_ptr, addr:req, size:req, limit:req, tmp:req
+#ifdef CONFIG_CPU_SPECTRE
+       sub     \tmp, \limit, #1
+       subs    \tmp, \tmp, \addr       @ tmp = limit - 1 - addr
+       addhs   \tmp, \tmp, #1          @ if (tmp >= 0) {
+       subshs  \tmp, \tmp, \size       @ tmp = limit - (addr + size) }
+       movlo   \addr, #0               @ if (tmp < 0) addr = NULL
+       csdb
+#endif
+       .endm
+
+       .macro  uaccess_disable, tmp, isb=1
+#ifdef CONFIG_CPU_SW_DOMAIN_PAN
+       /*
+        * Whenever we re-enter userspace, the domains should always be
+        * set appropriately.
+        */
+       mov     \tmp, #DACR_UACCESS_DISABLE
+       mcr     p15, 0, \tmp, c3, c0, 0         @ Set domain register
+       .if     \isb
+       instr_sync
+       .endif
+#endif
+       .endm
+
+       .macro  uaccess_enable, tmp, isb=1
+#ifdef CONFIG_CPU_SW_DOMAIN_PAN
+       /*
+        * Whenever we re-enter userspace, the domains should always be
+        * set appropriately.
+        */
+       mov     \tmp, #DACR_UACCESS_ENABLE
+       mcr     p15, 0, \tmp, c3, c0, 0
+       .if     \isb
+       instr_sync
+       .endif
+#endif
+       .endm
+
+#if defined(CONFIG_CPU_SW_DOMAIN_PAN) || defined(CONFIG_CPU_USE_DOMAINS)
+#define DACR(x...)     x
+#else
+#define DACR(x...)
+#endif
+
+       /*
+        * Save the address limit on entry to a privileged exception.
+        *
+        * If we are using the DACR for kernel access by the user accessors
+        * (CONFIG_CPU_USE_DOMAINS=y), always reset the DACR kernel domain
+        * back to client mode, whether or not \disable is set.
+        *
+        * If we are using SW PAN, set the DACR user domain to no access
+        * if \disable is set.
+        */
+       .macro  uaccess_entry, tsk, tmp0, tmp1, tmp2, disable
+       ldr     \tmp1, [\tsk, #TI_ADDR_LIMIT]
+       mov     \tmp2, #TASK_SIZE
+       str     \tmp2, [\tsk, #TI_ADDR_LIMIT]
+ DACR( mrc     p15, 0, \tmp0, c3, c0, 0)
+ DACR( str     \tmp0, [sp, #SVC_DACR])
+       str     \tmp1, [sp, #SVC_ADDR_LIMIT]
+       .if \disable && IS_ENABLED(CONFIG_CPU_SW_DOMAIN_PAN)
+       /* kernel=client, user=no access */
+       mov     \tmp2, #DACR_UACCESS_DISABLE
+       mcr     p15, 0, \tmp2, c3, c0, 0
+       instr_sync
+       .elseif IS_ENABLED(CONFIG_CPU_USE_DOMAINS)
+       /* kernel=client */
+       bic     \tmp2, \tmp0, #domain_mask(DOMAIN_KERNEL)
+       orr     \tmp2, \tmp2, #domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT)
+       mcr     p15, 0, \tmp2, c3, c0, 0
+       instr_sync
+       .endif
+       .endm
+
+       /* Restore the user access state previously saved by uaccess_entry */
+       .macro  uaccess_exit, tsk, tmp0, tmp1
+       ldr     \tmp1, [sp, #SVC_ADDR_LIMIT]
+ DACR( ldr     \tmp0, [sp, #SVC_DACR])
+       str     \tmp1, [\tsk, #TI_ADDR_LIMIT]
+ DACR( mcr     p15, 0, \tmp0, c3, c0, 0)
+       .endm
+
+#undef DACR
+
+#endif /* __ASM_UACCESS_ASM_H__ */
index 98bdea51089d59bd980725a63da030ace79bd1c4..82e96ac836849cf1be8d200ce7bf1d3b009173ca 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/export.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/cryptohash.h>
 #include <linux/delay.h>
 #include <linux/in6.h>
 #include <linux/syscalls.h>
index 4247ebf4b8934720a992b5e432bf5ed6bb260423..3c2faf2bd124e8f6383c247cf0ddff1f72a32dd0 100644 (file)
@@ -42,7 +42,7 @@ static int __init init_atags_procfs(void)
        size_t size;
 
        if (tag->hdr.tag != ATAG_CORE) {
-               pr_info("No ATAGs?");
+               pr_info("No ATAGs?\n");
                return -EINVAL;
        }
 
index 77f54830554c32599aa5de9a0e0fb52c4de5c5a6..55a47df0477383df6fd6447f89447a8ebd5598f4 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/unistd.h>
 #include <asm/tls.h>
 #include <asm/system_info.h>
+#include <asm/uaccess-asm.h>
 
 #include "entry-header.S"
 #include <asm/entry-macro-multi.S>
@@ -179,15 +180,7 @@ ENDPROC(__und_invalid)
        stmia   r7, {r2 - r6}
 
        get_thread_info tsk
-       ldr     r0, [tsk, #TI_ADDR_LIMIT]
-       mov     r1, #TASK_SIZE
-       str     r1, [tsk, #TI_ADDR_LIMIT]
-       str     r0, [sp, #SVC_ADDR_LIMIT]
-
-       uaccess_save r0
-       .if \uaccess
-       uaccess_disable r0
-       .endif
+       uaccess_entry tsk, r0, r1, r2, \uaccess
 
        .if \trace
 #ifdef CONFIG_TRACE_IRQFLAGS
index 32051ec5b33fa3dc41eb9c26e063cba7d502c048..40db0f9188b69e9e4323405c8c44e9a3fe153890 100644 (file)
@@ -6,6 +6,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/errno.h>
 #include <asm/thread_info.h>
+#include <asm/uaccess-asm.h>
 #include <asm/v7m.h>
 
 @ Bad Abort numbers
        blne    trace_hardirqs_off
 #endif
        .endif
-       ldr     r1, [sp, #SVC_ADDR_LIMIT]
-       uaccess_restore
-       str     r1, [tsk, #TI_ADDR_LIMIT]
+       uaccess_exit tsk, r0, r1
 
 #ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode SVC restore
        @ on the stack remains correct).
        @
        .macro  svc_exit_via_fiq
-       ldr     r1, [sp, #SVC_ADDR_LIMIT]
-       uaccess_restore
-       str     r1, [tsk, #TI_ADDR_LIMIT]
+       uaccess_exit tsk, r0, r1
 #ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode restore
        mov     r0, sp
index deef17f34bd277f55b384ca16686111a4b82e93c..af0a8500a24ead489fd46b77e450d76fc7d19bad 100644 (file)
@@ -55,6 +55,13 @@ void *module_alloc(unsigned long size)
 }
 #endif
 
+bool module_init_section(const char *name)
+{
+       return strstarts(name, ".init") ||
+               strstarts(name, ".ARM.extab.init") ||
+               strstarts(name, ".ARM.exidx.init");
+}
+
 bool module_exit_section(const char *name)
 {
        return strstarts(name, ".exit") ||
@@ -409,8 +416,17 @@ module_arch_cleanup(struct module *mod)
 #ifdef CONFIG_ARM_UNWIND
        int i;
 
-       for (i = 0; i < ARM_SEC_MAX; i++)
-               if (mod->arch.unwind[i])
-                       unwind_table_del(mod->arch.unwind[i]);
+       for (i = 0; i < ARM_SEC_MAX; i++) {
+               unwind_table_del(mod->arch.unwind[i]);
+               mod->arch.unwind[i] = NULL;
+       }
+#endif
+}
+
+void __weak module_arch_freeing_init(struct module *mod)
+{
+#ifdef CONFIG_ARM_UNWIND
+       unwind_table_del(mod->arch.unwind[ARM_SEC_INIT]);
+       mod->arch.unwind[ARM_SEC_INIT] = NULL;
 #endif
 }
index b606cded90cd52f1c49f864e8013908290dc0fcb..4cc6a7eff6359a23633587742bef6e3690e76ecf 100644 (file)
@@ -219,8 +219,8 @@ static struct undef_hook arm_break_hook = {
 };
 
 static struct undef_hook thumb_break_hook = {
-       .instr_mask     = 0xffff,
-       .instr_val      = 0xde01,
+       .instr_mask     = 0xffffffff,
+       .instr_val      = 0x0000de01,
        .cpsr_mask      = PSR_T_BIT,
        .cpsr_val       = PSR_T_BIT,
        .fn             = break_trap,
index 17bd32b2237165fd7b2ae9737c264206ef261894..0203e545bbc8df4cd7cc3f5716d78ace12c27bd0 100644 (file)
@@ -253,20 +253,15 @@ asmlinkage long sys_oabi_epoll_ctl(int epfd, int op, int fd,
 {
        struct oabi_epoll_event user;
        struct epoll_event kernel;
-       mm_segment_t fs;
-       long ret;
 
-       if (op == EPOLL_CTL_DEL)
-               return sys_epoll_ctl(epfd, op, fd, NULL);
-       if (copy_from_user(&user, event, sizeof(user)))
+       if (ep_op_has_event(op) &&
+           copy_from_user(&user, event, sizeof(user)))
                return -EFAULT;
+
        kernel.events = user.events;
        kernel.data   = user.data;
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-       ret = sys_epoll_ctl(epfd, op, fd, &kernel);
-       set_fs(fs);
-       return ret;
+
+       return do_epoll_ctl(epfd, op, fd, &kernel, false);
 }
 
 asmlinkage long sys_oabi_epoll_wait(int epfd,
index 5461d589a1e25e7b63207f83968f63429860263b..60ac7c5999a98eb3046d32e17299cc42dfd980ec 100644 (file)
@@ -5,6 +5,7 @@
  *  VMA_VM_FLAGS
  *  VM_EXEC
  */
+#include <linux/const.h>
 #include <asm/asm-offsets.h>
 #include <asm/thread_info.h>
 
@@ -30,7 +31,7 @@
  * act_mm - get current->active_mm
  */
        .macro  act_mm, rd
-       bic     \rd, sp, #8128
+       bic     \rd, sp, #(THREAD_SIZE - 1) & ~63
        bic     \rd, \rd, #63
        ldr     \rd, [\rd, #TI_TASK]
        .if (TSK_ACTIVE_MM > IMM12_MASK)
index 4d1cf74a2caac645c4b217134a066a12bcbe7056..d5cae5ffede0cd6e38846407b9fdce94fd352752 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 5d513f461957b5e416113bada2688a8ee7f34179..552d36cacc051c6a0caa67e27c956164dd20c24f 100644 (file)
@@ -9,6 +9,7 @@ config ARM64
        select ACPI_MCFG if (ACPI && PCI)
        select ACPI_SPCR_TABLE if ACPI
        select ACPI_PPTT if ACPI
+       select ARCH_BINFMT_ELF_STATE
        select ARCH_HAS_DEBUG_VIRTUAL
        select ARCH_HAS_DEVMEM_IS_ALLOWED
        select ARCH_HAS_DMA_PREP_COHERENT
@@ -33,6 +34,7 @@ config ARM64
        select ARCH_HAS_SYSCALL_WRAPPER
        select ARCH_HAS_TEARDOWN_DMA_OPS if IOMMU_SUPPORT
        select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
+       select ARCH_HAVE_ELF_PROT
        select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select ARCH_INLINE_READ_LOCK if !PREEMPTION
        select ARCH_INLINE_READ_LOCK_BH if !PREEMPTION
@@ -62,9 +64,12 @@ config ARM64
        select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
        select ARCH_KEEP_MEMBLOCK
        select ARCH_USE_CMPXCHG_LOCKREF
+       select ARCH_USE_GNU_PROPERTY
        select ARCH_USE_QUEUED_RWLOCKS
        select ARCH_USE_QUEUED_SPINLOCKS
+       select ARCH_USE_SYM_ANNOTATIONS
        select ARCH_SUPPORTS_MEMORY_FAILURE
+       select ARCH_SUPPORTS_SHADOW_CALL_STACK if CC_HAVE_SHADOW_CALL_STACK
        select ARCH_SUPPORTS_ATOMIC_RMW
        select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && (GCC_VERSION >= 50000 || CC_IS_CLANG)
        select ARCH_SUPPORTS_NUMA_BALANCING
@@ -525,13 +530,13 @@ config ARM64_ERRATUM_1418040
 
          If unsure, say Y.
 
-config ARM64_WORKAROUND_SPECULATIVE_AT_VHE
+config ARM64_WORKAROUND_SPECULATIVE_AT
        bool
 
 config ARM64_ERRATUM_1165522
-       bool "Cortex-A76: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
+       bool "Cortex-A76: 1165522: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
        default y
-       select ARM64_WORKAROUND_SPECULATIVE_AT_VHE
+       select ARM64_WORKAROUND_SPECULATIVE_AT
        help
          This option adds a workaround for ARM Cortex-A76 erratum 1165522.
 
@@ -541,10 +546,23 @@ config ARM64_ERRATUM_1165522
 
          If unsure, say Y.
 
+config ARM64_ERRATUM_1319367
+       bool "Cortex-A57/A72: 1319537: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
+       default y
+       select ARM64_WORKAROUND_SPECULATIVE_AT
+       help
+         This option adds work arounds for ARM Cortex-A57 erratum 1319537
+         and A72 erratum 1319367
+
+         Cortex-A57 and A72 cores could end-up with corrupted TLBs by
+         speculating an AT instruction during a guest context switch.
+
+         If unsure, say Y.
+
 config ARM64_ERRATUM_1530923
-       bool "Cortex-A55: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
+       bool "Cortex-A55: 1530923: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
        default y
-       select ARM64_WORKAROUND_SPECULATIVE_AT_VHE
+       select ARM64_WORKAROUND_SPECULATIVE_AT
        help
          This option adds a workaround for ARM Cortex-A55 erratum 1530923.
 
@@ -554,6 +572,9 @@ config ARM64_ERRATUM_1530923
 
          If unsure, say Y.
 
+config ARM64_WORKAROUND_REPEAT_TLBI
+       bool
+
 config ARM64_ERRATUM_1286807
        bool "Cortex-A76: Modification of the translation table for a virtual address might lead to read-after-read ordering violation"
        default y
@@ -570,22 +591,6 @@ config ARM64_ERRATUM_1286807
          invalidated has been observed by other observers. The
          workaround repeats the TLBI+DSB operation.
 
-config ARM64_WORKAROUND_SPECULATIVE_AT_NVHE
-       bool
-
-config ARM64_ERRATUM_1319367
-       bool "Cortex-A57/A72: Speculative AT instruction using out-of-context translation regime could cause subsequent request to generate an incorrect translation"
-       default y
-       select ARM64_WORKAROUND_SPECULATIVE_AT_NVHE
-       help
-         This option adds work arounds for ARM Cortex-A57 erratum 1319537
-         and A72 erratum 1319367
-
-         Cortex-A57 and A72 cores could end-up with corrupted TLBs by
-         speculating an AT instruction during a guest context switch.
-
-         If unsure, say Y.
-
 config ARM64_ERRATUM_1463225
        bool "Cortex-A76: Software Step might prevent interrupt recognition"
        default y
@@ -695,6 +700,35 @@ config CAVIUM_TX2_ERRATUM_219
 
          If unsure, say Y.
 
+config FUJITSU_ERRATUM_010001
+       bool "Fujitsu-A64FX erratum E#010001: Undefined fault may occur wrongly"
+       default y
+       help
+         This option adds a workaround for Fujitsu-A64FX erratum E#010001.
+         On some variants of the Fujitsu-A64FX cores ver(1.0, 1.1), memory
+         accesses may cause undefined fault (Data abort, DFSC=0b111111).
+         This fault occurs under a specific hardware condition when a
+         load/store instruction performs an address translation using:
+         case-1  TTBR0_EL1 with TCR_EL1.NFD0 == 1.
+         case-2  TTBR0_EL2 with TCR_EL2.NFD0 == 1.
+         case-3  TTBR1_EL1 with TCR_EL1.NFD1 == 1.
+         case-4  TTBR1_EL2 with TCR_EL2.NFD1 == 1.
+
+         The workaround is to ensure these bits are clear in TCR_ELx.
+         The workaround only affects the Fujitsu-A64FX.
+
+         If unsure, say Y.
+
+config HISILICON_ERRATUM_161600802
+       bool "Hip07 161600802: Erroneous redistributor VLPI base"
+       default y
+       help
+         The HiSilicon Hip07 SoC uses the wrong redistributor base
+         when issued ITS commands such as VMOVP and VMAPP, and requires
+         a 128kB offset to be applied to the target address in this commands.
+
+         If unsure, say Y.
+
 config QCOM_FALKOR_ERRATUM_1003
        bool "Falkor E1003: Incorrect translation due to ASID change"
        default y
@@ -706,9 +740,6 @@ config QCOM_FALKOR_ERRATUM_1003
          is unchanged. Work around the erratum by invalidating the walk cache
          entries for the trampoline before entering the kernel proper.
 
-config ARM64_WORKAROUND_REPEAT_TLBI
-       bool
-
 config QCOM_FALKOR_ERRATUM_1009
        bool "Falkor E1009: Prematurely complete a DSB after a TLBI"
        default y
@@ -730,25 +761,6 @@ config QCOM_QDF2400_ERRATUM_0065
 
          If unsure, say Y.
 
-config SOCIONEXT_SYNQUACER_PREITS
-       bool "Socionext Synquacer: Workaround for GICv3 pre-ITS"
-       default y
-       help
-         Socionext Synquacer SoCs implement a separate h/w block to generate
-         MSI doorbell writes with non-zero values for the device ID.
-
-         If unsure, say Y.
-
-config HISILICON_ERRATUM_161600802
-       bool "Hip07 161600802: Erroneous redistributor VLPI base"
-       default y
-       help
-         The HiSilicon Hip07 SoC uses the wrong redistributor base
-         when issued ITS commands such as VMOVP and VMAPP, and requires
-         a 128kB offset to be applied to the target address in this commands.
-
-         If unsure, say Y.
-
 config QCOM_FALKOR_ERRATUM_E1041
        bool "Falkor E1041: Speculative instruction fetches might cause errant memory access"
        default y
@@ -759,22 +771,12 @@ config QCOM_FALKOR_ERRATUM_E1041
 
          If unsure, say Y.
 
-config FUJITSU_ERRATUM_010001
-       bool "Fujitsu-A64FX erratum E#010001: Undefined fault may occur wrongly"
+config SOCIONEXT_SYNQUACER_PREITS
+       bool "Socionext Synquacer: Workaround for GICv3 pre-ITS"
        default y
        help
-         This option adds a workaround for Fujitsu-A64FX erratum E#010001.
-         On some variants of the Fujitsu-A64FX cores ver(1.0, 1.1), memory
-         accesses may cause undefined fault (Data abort, DFSC=0b111111).
-         This fault occurs under a specific hardware condition when a
-         load/store instruction performs an address translation using:
-         case-1  TTBR0_EL1 with TCR_EL1.NFD0 == 1.
-         case-2  TTBR0_EL2 with TCR_EL2.NFD0 == 1.
-         case-3  TTBR1_EL1 with TCR_EL1.NFD1 == 1.
-         case-4  TTBR1_EL2 with TCR_EL2.NFD1 == 1.
-
-         The workaround is to ensure these bits are clear in TCR_ELx.
-         The workaround only affects the Fujitsu-A64FX.
+         Socionext Synquacer SoCs implement a separate h/w block to generate
+         MSI doorbell writes with non-zero values for the device ID.
 
          If unsure, say Y.
 
@@ -1026,6 +1028,10 @@ config ARCH_HAS_CACHE_LINE_SIZE
 config ARCH_ENABLE_SPLIT_PMD_PTLOCK
        def_bool y if PGTABLE_LEVELS > 2
 
+# Supported by clang >= 7.0
+config CC_HAVE_SHADOW_CALL_STACK
+       def_bool $(cc-option, -fsanitize=shadow-call-stack -ffixed-x18)
+
 config SECCOMP
        bool "Enable seccomp to safely compute untrusted bytecode"
        ---help---
@@ -1585,6 +1591,48 @@ endmenu
 
 menu "ARMv8.5 architectural features"
 
+config ARM64_BTI
+       bool "Branch Target Identification support"
+       default y
+       help
+         Branch Target Identification (part of the ARMv8.5 Extensions)
+         provides a mechanism to limit the set of locations to which computed
+         branch instructions such as BR or BLR can jump.
+
+         To make use of BTI on CPUs that support it, say Y.
+
+         BTI is intended to provide complementary protection to other control
+         flow integrity protection mechanisms, such as the Pointer
+         authentication mechanism provided as part of the ARMv8.3 Extensions.
+         For this reason, it does not make sense to enable this option without
+         also enabling support for pointer authentication.  Thus, when
+         enabling this option you should also select ARM64_PTR_AUTH=y.
+
+         Userspace binaries must also be specifically compiled to make use of
+         this mechanism.  If you say N here or the hardware does not support
+         BTI, such binaries can still run, but you get no additional
+         enforcement of branch destinations.
+
+config ARM64_BTI_KERNEL
+       bool "Use Branch Target Identification for kernel"
+       default y
+       depends on ARM64_BTI
+       depends on ARM64_PTR_AUTH
+       depends on CC_HAS_BRANCH_PROT_PAC_RET_BTI
+       # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94697
+       depends on !CC_IS_GCC || GCC_VERSION >= 100100
+       depends on !(CC_IS_CLANG && GCOV_KERNEL)
+       depends on (!FUNCTION_GRAPH_TRACER || DYNAMIC_FTRACE_WITH_REGS)
+       help
+         Build the kernel with Branch Target Identification annotations
+         and enable enforcement of this for kernel code. When this option
+         is enabled and the system supports BTI all kernel code including
+         modular code must have BTI enabled.
+
+config CC_HAS_BRANCH_PROT_PAC_RET_BTI
+       # GCC 9 or later, clang 8 or later
+       def_bool $(cc-option,-mbranch-protection=pac-ret+leaf+bti)
+
 config ARM64_E0PD
        bool "Enable support for E0PD"
        default y
@@ -1786,7 +1834,7 @@ config EFI
        select EFI_PARAMS_FROM_FDT
        select EFI_RUNTIME_WRAPPERS
        select EFI_STUB
-       select EFI_ARMSTUB
+       select EFI_GENERIC_STUB
        default y
        help
          This option provides support for runtime services provided
index 85e4149cc5d5c142d20b2924d3e10f31cf2a240d..650e1185c190b3b4c530038083da602c06b7976c 100644 (file)
@@ -12,7 +12,6 @@
 
 LDFLAGS_vmlinux        :=--no-undefined -X
 CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET)
-GZFLAGS                :=-9
 
 ifeq ($(CONFIG_RELOCATABLE), y)
 # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour
@@ -71,7 +70,14 @@ branch-prot-flags-y += $(call cc-option,-mbranch-protection=none)
 
 ifeq ($(CONFIG_ARM64_PTR_AUTH),y)
 branch-prot-flags-$(CONFIG_CC_HAS_SIGN_RETURN_ADDRESS) := -msign-return-address=all
+# We enable additional protection for leaf functions as there is some
+# narrow potential for ROP protection benefits and no substantial
+# performance impact has been observed.
+ifeq ($(CONFIG_ARM64_BTI_KERNEL),y)
+branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI) := -mbranch-protection=pac-ret+leaf+bti
+else
 branch-prot-flags-$(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET) := -mbranch-protection=pac-ret+leaf
+endif
 # -march=armv8.3-a enables the non-nops instructions for PAC, to avoid the
 # compiler to generate them and consequently to break the single image contract
 # we pass it only to the assembler. This option is utilized only in case of non
@@ -81,6 +87,10 @@ endif
 
 KBUILD_CFLAGS += $(branch-prot-flags-y)
 
+ifeq ($(CONFIG_SHADOW_CALL_STACK), y)
+KBUILD_CFLAGS  += -ffixed-x18
+endif
+
 ifeq ($(CONFIG_CPU_BIG_ENDIAN), y)
 KBUILD_CPPFLAGS        += -mbig-endian
 CHECKFLAGS     += -D__AARCH64EB__
@@ -118,7 +128,7 @@ TEXT_OFFSET := $(shell awk "BEGIN {srand(); printf \"0x%06x\n\", \
                 int(2 * 1024 * 1024 / (2 ^ $(CONFIG_ARM64_PAGE_SHIFT)) * \
                 rand()) * (2 ^ $(CONFIG_ARM64_PAGE_SHIFT))}")
 else
-TEXT_OFFSET := 0x00080000
+TEXT_OFFSET := 0x0
 endif
 
 ifeq ($(CONFIG_KASAN_SW_TAGS), y)
@@ -131,7 +141,7 @@ KBUILD_CFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 KBUILD_CPPFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 KBUILD_AFLAGS += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT)
 
-export TEXT_OFFSET GZFLAGS
+export TEXT_OFFSET
 
 core-y         += arch/arm64/
 libs-y         := arch/arm64/lib/ $(libs-y)
index ccb8e88a60c5a5b2512558a876f1c1b1c20558b5..d819e44d94a8d12a8747619c78916d06104c05bd 100644 (file)
                                      "venc_lt_sel";
                        assigned-clocks = <&topckgen CLK_TOP_VENC_SEL>,
                                          <&topckgen CLK_TOP_VENC_LT_SEL>;
-                       assigned-clock-parents = <&topckgen CLK_TOP_VENCPLL_D2>,
-                                                <&topckgen CLK_TOP_UNIVPLL1_D2>;
+                       assigned-clock-parents = <&topckgen CLK_TOP_VCODECPLL>,
+                                                <&topckgen CLK_TOP_VCODECPLL_370P5>;
                };
 
                jpegdec: jpegdec@18004000 {
index ed5409c6abf4e99183d3a59b19a8f11e766b80ca..395bbf64b2abb31b9be13856e59f2deb4c90aa79 100644 (file)
@@ -158,7 +158,6 @@ static int __maybe_unused essiv_cbc_set_key(struct crypto_skcipher *tfm,
                                            unsigned int key_len)
 {
        struct crypto_aes_essiv_cbc_ctx *ctx = crypto_skcipher_ctx(tfm);
-       SHASH_DESC_ON_STACK(desc, ctx->hash);
        u8 digest[SHA256_DIGEST_SIZE];
        int ret;
 
@@ -166,8 +165,7 @@ static int __maybe_unused essiv_cbc_set_key(struct crypto_skcipher *tfm,
        if (ret)
                return ret;
 
-       desc->tfm = ctx->hash;
-       crypto_shash_digest(desc, in_key, key_len, digest);
+       crypto_shash_tfm_digest(ctx->hash, in_key, key_len, digest);
 
        return aes_expandkey(&ctx->key2, digest, sizeof(digest));
 }
index 5a95c2628fbf087b79b790a2bb73f21d3b18ada1..111d9c9abddd1885785c135cfb4a4f07ff8ab807 100644 (file)
@@ -66,7 +66,7 @@
 #include <asm/assembler.h>
 
        .text
-       .cpu            generic+crypto
+       .arch           armv8-a+crypto
 
        init_crc        .req    w19
        buf             .req    x20
index ddf4a0d85c1c2083a3fff1fe485a70a4bae2a79b..77bc6e72abae9457af778dc48c4eecca8f54611c 100644 (file)
@@ -12,7 +12,6 @@
 #include <crypto/internal/simd.h>
 #include <crypto/sha.h>
 #include <crypto/sha256_base.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <linux/string.h>
 
index 78d3083de6b7339ad3ee06d57028ce1331133bd0..370ccb29602fda88772f8941f08d7a86e9bfe1d3 100644 (file)
@@ -6,7 +6,6 @@
  */
 
 #include <crypto/internal/hash.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <crypto/sha.h>
index ce2a8486992bb86264c247477a18b37eb3d74796..52dead2a8640d2ef4acf30a0059e2d2c3413e906 100644 (file)
@@ -39,25 +39,58 @@ alternative_if ARM64_HAS_GENERIC_AUTH
 alternative_else_nop_endif
        .endm
 
-       .macro ptrauth_keys_install_kernel tsk, sync, tmp1, tmp2, tmp3
-alternative_if ARM64_HAS_ADDRESS_AUTH
+       .macro __ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3
        mov     \tmp1, #THREAD_KEYS_KERNEL
        add     \tmp1, \tsk, \tmp1
        ldp     \tmp2, \tmp3, [\tmp1, #PTRAUTH_KERNEL_KEY_APIA]
        msr_s   SYS_APIAKEYLO_EL1, \tmp2
        msr_s   SYS_APIAKEYHI_EL1, \tmp3
-       .if     \sync == 1
+       .endm
+
+       .macro ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3
+alternative_if ARM64_HAS_ADDRESS_AUTH
+       __ptrauth_keys_install_kernel_nosync \tsk, \tmp1, \tmp2, \tmp3
+alternative_else_nop_endif
+       .endm
+
+       .macro ptrauth_keys_install_kernel tsk, tmp1, tmp2, tmp3
+alternative_if ARM64_HAS_ADDRESS_AUTH
+       __ptrauth_keys_install_kernel_nosync \tsk, \tmp1, \tmp2, \tmp3
        isb
-       .endif
 alternative_else_nop_endif
        .endm
 
+       .macro __ptrauth_keys_init_cpu tsk, tmp1, tmp2, tmp3
+       mrs     \tmp1, id_aa64isar1_el1
+       ubfx    \tmp1, \tmp1, #ID_AA64ISAR1_APA_SHIFT, #8
+       cbz     \tmp1, .Lno_addr_auth\@
+       mov_q   \tmp1, (SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | \
+                       SCTLR_ELx_ENDA | SCTLR_ELx_ENDB)
+       mrs     \tmp2, sctlr_el1
+       orr     \tmp2, \tmp2, \tmp1
+       msr     sctlr_el1, \tmp2
+       __ptrauth_keys_install_kernel_nosync \tsk, \tmp1, \tmp2, \tmp3
+       isb
+.Lno_addr_auth\@:
+       .endm
+
+       .macro ptrauth_keys_init_cpu tsk, tmp1, tmp2, tmp3
+alternative_if_not ARM64_HAS_ADDRESS_AUTH
+       b       .Lno_addr_auth\@
+alternative_else_nop_endif
+       __ptrauth_keys_init_cpu \tsk, \tmp1, \tmp2, \tmp3
+.Lno_addr_auth\@:
+       .endm
+
 #else /* CONFIG_ARM64_PTR_AUTH */
 
        .macro ptrauth_keys_install_user tsk, tmp1, tmp2, tmp3
        .endm
 
-       .macro ptrauth_keys_install_kernel tsk, sync, tmp1, tmp2, tmp3
+       .macro ptrauth_keys_install_kernel_nosync tsk, tmp1, tmp2, tmp3
+       .endm
+
+       .macro ptrauth_keys_install_kernel tsk, tmp1, tmp2, tmp3
        .endm
 
 #endif /* CONFIG_ARM64_PTR_AUTH */
index 0bff325117b4223ee094411fcfcf9bec74169081..54d1811776566e5ba5fe9410aec9f1c031b20c1f 100644 (file)
@@ -736,4 +736,54 @@ USER(\label, ic    ivau, \tmp2)                    // invalidate I line PoU
 .Lyield_out_\@ :
        .endm
 
+/*
+ * This macro emits a program property note section identifying
+ * architecture features which require special handling, mainly for
+ * use in assembly files included in the VDSO.
+ */
+
+#define NT_GNU_PROPERTY_TYPE_0  5
+#define GNU_PROPERTY_AARCH64_FEATURE_1_AND      0xc0000000
+
+#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI      (1U << 0)
+#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC      (1U << 1)
+
+#ifdef CONFIG_ARM64_BTI_KERNEL
+#define GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT         \
+               ((GNU_PROPERTY_AARCH64_FEATURE_1_BTI |  \
+                 GNU_PROPERTY_AARCH64_FEATURE_1_PAC))
+#endif
+
+#ifdef GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT
+.macro emit_aarch64_feature_1_and, feat=GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT
+       .pushsection .note.gnu.property, "a"
+       .align  3
+       .long   2f - 1f
+       .long   6f - 3f
+       .long   NT_GNU_PROPERTY_TYPE_0
+1:      .string "GNU"
+2:
+       .align  3
+3:      .long   GNU_PROPERTY_AARCH64_FEATURE_1_AND
+       .long   5f - 4f
+4:
+       /*
+        * This is described with an array of char in the Linux API
+        * spec but the text and all other usage (including binutils,
+        * clang and GCC) treat this as a 32 bit value so no swizzling
+        * is required for big endian.
+        */
+       .long   \feat
+5:
+       .align  3
+6:
+       .popsection
+.endm
+
+#else
+.macro emit_aarch64_feature_1_and, feat=0
+.endm
+
+#endif /* GNU_PROPERTY_AARCH64_FEATURE_1_DEFAULT */
+
 #endif /* __ASM_ASSEMBLER_H */
index e6cca3d4acf702f060bb35996ead0762566de7b6..ce50c1f1f1ea75975f069bd6a1e4fae1c04ba281 100644 (file)
@@ -79,7 +79,7 @@ static inline void flush_icache_range(unsigned long start, unsigned long end)
         * IPI all online CPUs so that they undergo a context synchronization
         * event and are forced to refetch the new instructions.
         */
-#ifdef CONFIG_KGDB
+
        /*
         * KGDB performs cache maintenance with interrupts disabled, so we
         * will deadlock trying to IPI the secondary CPUs. In theory, we can
@@ -89,9 +89,9 @@ static inline void flush_icache_range(unsigned long start, unsigned long end)
         * the patching operation, so we don't need extra IPIs here anyway.
         * In which case, add a KGDB-specific bodge and return early.
         */
-       if (kgdb_connected && irqs_disabled())
+       if (in_dbg_master())
                return;
-#endif
+
        kick_all_cpus_sync();
 }
 
index eece20d2c55fcbf2f24fd309267bb555b642a6d3..51a7ce87cdfe07c55409d766118dfedda56d9535 100644 (file)
@@ -2,8 +2,6 @@
 #ifndef __ASM_COMPILER_H
 #define __ASM_COMPILER_H
 
-#if defined(CONFIG_ARM64_PTR_AUTH)
-
 /*
  * The EL0/EL1 pointer bits used by a pointer authentication code.
  * This is dependent on TBI0/TBI1 being enabled, or bits 63:56 would also apply.
@@ -19,6 +17,4 @@
 #define __builtin_return_address(val)                                  \
        (void *)(ptrauth_clear_pac((unsigned long)__builtin_return_address(val)))
 
-#endif /* CONFIG_ARM64_PTR_AUTH */
-
 #endif /* __ASM_COMPILER_H */
index b4a40535a3d8281a1154e7bd3c08199504157ce1..7faae6ff3ab4d5c27896305d609494cdb13d54e6 100644 (file)
@@ -33,6 +33,7 @@ struct cpuinfo_arm64 {
        u64             reg_id_aa64zfr0;
 
        u32             reg_id_dfr0;
+       u32             reg_id_dfr1;
        u32             reg_id_isar0;
        u32             reg_id_isar1;
        u32             reg_id_isar2;
@@ -44,8 +45,11 @@ struct cpuinfo_arm64 {
        u32             reg_id_mmfr1;
        u32             reg_id_mmfr2;
        u32             reg_id_mmfr3;
+       u32             reg_id_mmfr4;
+       u32             reg_id_mmfr5;
        u32             reg_id_pfr0;
        u32             reg_id_pfr1;
+       u32             reg_id_pfr2;
 
        u32             reg_mvfr0;
        u32             reg_mvfr1;
index 8eb5a088ae6588c8c0f2332b31ca92945eda9b66..d7b3bb0cb180462fa260eaf58f49117760a020ea 100644 (file)
@@ -44,7 +44,7 @@
 #define ARM64_SSBS                             34
 #define ARM64_WORKAROUND_1418040               35
 #define ARM64_HAS_SB                           36
-#define ARM64_WORKAROUND_SPECULATIVE_AT_VHE    37
+#define ARM64_WORKAROUND_SPECULATIVE_AT                37
 #define ARM64_HAS_ADDRESS_AUTH_ARCH            38
 #define ARM64_HAS_ADDRESS_AUTH_IMP_DEF         39
 #define ARM64_HAS_GENERIC_AUTH_ARCH            40
 #define ARM64_WORKAROUND_CAVIUM_TX2_219_TVM    45
 #define ARM64_WORKAROUND_CAVIUM_TX2_219_PRFM   46
 #define ARM64_WORKAROUND_1542419               47
-#define ARM64_WORKAROUND_SPECULATIVE_AT_NVHE   48
-#define ARM64_HAS_E0PD                         49
-#define ARM64_HAS_RNG                          50
-#define ARM64_HAS_AMU_EXTN                     51
-#define ARM64_HAS_ADDRESS_AUTH                 52
-#define ARM64_HAS_GENERIC_AUTH                 53
+#define ARM64_HAS_E0PD                         48
+#define ARM64_HAS_RNG                          49
+#define ARM64_HAS_AMU_EXTN                     50
+#define ARM64_HAS_ADDRESS_AUTH                 51
+#define ARM64_HAS_GENERIC_AUTH                 52
+#define ARM64_HAS_32BIT_EL1                    53
+#define ARM64_BTI                              54
 
-#define ARM64_NCAPS                            54
+#define ARM64_NCAPS                            55
 
 #endif /* __ASM_CPUCAPS_H */
index afe08251ff95640818a89453db51d2127258a11c..5d1f4ae42799b2a86459c5892d633b24d09939e1 100644 (file)
@@ -551,6 +551,13 @@ static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
                cpuid_feature_extract_unsigned_field(mmfr0, ID_AA64MMFR0_BIGENDEL0_SHIFT) == 0x1;
 }
 
+static inline bool id_aa64pfr0_32bit_el1(u64 pfr0)
+{
+       u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL1_SHIFT);
+
+       return val == ID_AA64PFR0_EL1_32BIT_64BIT;
+}
+
 static inline bool id_aa64pfr0_32bit_el0(u64 pfr0)
 {
        u32 val = cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_EL0_SHIFT);
@@ -680,6 +687,11 @@ static inline bool system_has_prio_mask_debugging(void)
               system_uses_irq_prio_masking();
 }
 
+static inline bool system_supports_bti(void)
+{
+       return IS_ENABLED(CONFIG_ARM64_BTI) && cpus_have_const_cap(ARM64_BTI);
+}
+
 #define ARM64_BP_HARDEN_UNKNOWN                -1
 #define ARM64_BP_HARDEN_WA_NEEDED      0
 #define ARM64_BP_HARDEN_NOT_REQUIRED   1
@@ -745,6 +757,24 @@ static inline bool cpu_has_hw_af(void)
 extern bool cpu_has_amu_feat(int cpu);
 #endif
 
+static inline unsigned int get_vmid_bits(u64 mmfr1)
+{
+       int vmid_bits;
+
+       vmid_bits = cpuid_feature_extract_unsigned_field(mmfr1,
+                                               ID_AA64MMFR1_VMIDBITS_SHIFT);
+       if (vmid_bits == ID_AA64MMFR1_VMIDBITS_16)
+               return 16;
+
+       /*
+        * Return the default here even if any reserved
+        * value is fetched from the system register.
+        */
+       return 8;
+}
+
+u32 get_kvm_ipa_limit(void);
+
 #endif /* __ASSEMBLY__ */
 
 #endif
index 7619f473155f2fac2dabfc9ebddfcb797475f285..e5ceea213e39a580c932e16b994922de3cfd7915 100644 (file)
@@ -125,5 +125,7 @@ static inline int reinstall_suspended_bps(struct pt_regs *regs)
 
 int aarch32_break_handler(struct pt_regs *regs);
 
+void debug_traps_init(void);
+
 #endif /* __ASSEMBLY */
 #endif /* __ASM_DEBUG_MONITORS_H */
index 45e8212227743a7b4019e308d0ecc390cc4630df..d4ab3f73e7a350810b148e849d227f70bdc305d7 100644 (file)
@@ -86,14 +86,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
        return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__)
-#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__)
-#define efi_is_native()                (true)
-
-#define efi_table_attr(inst, attr)     (inst->attr)
-
-#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
-
 #define alloc_screen_info(x...)                &screen_info
 
 static inline void free_screen_info(struct screen_info *si)
index b618017205a3f6af4ad612310d0187abdc2d73c1..4f00d50585a4c055756800df1408ddd6d4c4e98b 100644 (file)
 
 #ifndef __ASSEMBLY__
 
+#include <uapi/linux/elf.h>
 #include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/types.h>
 #include <asm/processor.h> /* for signal_minsigstksz, used by ARCH_DLINFO */
 
 typedef unsigned long elf_greg_t;
@@ -224,6 +228,52 @@ extern int aarch32_setup_additional_pages(struct linux_binprm *bprm,
 
 #endif /* CONFIG_COMPAT */
 
+struct arch_elf_state {
+       int flags;
+};
+
+#define ARM64_ELF_BTI          (1 << 0)
+
+#define INIT_ARCH_ELF_STATE {                  \
+       .flags = 0,                             \
+}
+
+static inline int arch_parse_elf_property(u32 type, const void *data,
+                                         size_t datasz, bool compat,
+                                         struct arch_elf_state *arch)
+{
+       /* No known properties for AArch32 yet */
+       if (IS_ENABLED(CONFIG_COMPAT) && compat)
+               return 0;
+
+       if (type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) {
+               const u32 *p = data;
+
+               if (datasz != sizeof(*p))
+                       return -ENOEXEC;
+
+               if (system_supports_bti() &&
+                   (*p & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+                       arch->flags |= ARM64_ELF_BTI;
+       }
+
+       return 0;
+}
+
+static inline int arch_elf_pt_proc(void *ehdr, void *phdr,
+                                  struct file *f, bool is_interp,
+                                  struct arch_elf_state *state)
+{
+       return 0;
+}
+
+static inline int arch_check_elf(void *ehdr, bool has_interp,
+                                void *interp_ehdr,
+                                struct arch_elf_state *state)
+{
+       return 0;
+}
+
 #endif /* !__ASSEMBLY__ */
 
 #endif
index 6a395a7e6707bae66291b7ce2c2cd81f2003f262..035003acfa876dd998c56d842ea48e882aa44638 100644 (file)
@@ -22,7 +22,7 @@
 #define ESR_ELx_EC_PAC         (0x09)  /* EL2 and above */
 /* Unallocated EC: 0x0A - 0x0B */
 #define ESR_ELx_EC_CP14_64     (0x0C)
-/* Unallocated EC: 0x0d */
+#define ESR_ELx_EC_BTI         (0x0D)
 #define ESR_ELx_EC_ILL         (0x0E)
 /* Unallocated EC: 0x0F - 0x10 */
 #define ESR_ELx_EC_SVC32       (0x11)
index 7a6e81ca23a8e0ed5a11013fc74f3ca82cefd1c5..7577a754d44343b26c700811c54b9155893b1322 100644 (file)
@@ -34,6 +34,7 @@ static inline u32 disr_to_esr(u64 disr)
 asmlinkage void enter_from_user_mode(void);
 void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
 void do_undefinstr(struct pt_regs *regs);
+void do_bti(struct pt_regs *regs);
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
 void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
                        struct pt_regs *regs);
index 87ad961f3c9753c30a33d1d8c8baac050d75612b..985493af704b5b035b6adfb470c7dec6159e2cb1 100644 (file)
@@ -32,30 +32,70 @@ u64 smp_irq_stat_cpu(unsigned int cpu);
 
 struct nmi_ctx {
        u64 hcr;
+       unsigned int cnt;
 };
 
 DECLARE_PER_CPU(struct nmi_ctx, nmi_contexts);
 
-#define arch_nmi_enter()                                                       \
-       do {                                                                    \
-               if (is_kernel_in_hyp_mode()) {                                  \
-                       struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts);  \
-                       nmi_ctx->hcr = read_sysreg(hcr_el2);                    \
-                       if (!(nmi_ctx->hcr & HCR_TGE)) {                        \
-                               write_sysreg(nmi_ctx->hcr | HCR_TGE, hcr_el2);  \
-                               isb();                                          \
-                       }                                                       \
-               }                                                               \
-       } while (0)
+#define arch_nmi_enter()                                               \
+do {                                                                   \
+       struct nmi_ctx *___ctx;                                         \
+       u64 ___hcr;                                                     \
+                                                                       \
+       if (!is_kernel_in_hyp_mode())                                   \
+               break;                                                  \
+                                                                       \
+       ___ctx = this_cpu_ptr(&nmi_contexts);                           \
+       if (___ctx->cnt) {                                              \
+               ___ctx->cnt++;                                          \
+               break;                                                  \
+       }                                                               \
+                                                                       \
+       ___hcr = read_sysreg(hcr_el2);                                  \
+       if (!(___hcr & HCR_TGE)) {                                      \
+               write_sysreg(___hcr | HCR_TGE, hcr_el2);                \
+               isb();                                                  \
+       }                                                               \
+       /*                                                              \
+        * Make sure the sysreg write is performed before ___ctx->cnt   \
+        * is set to 1. NMIs that see cnt == 1 will rely on us.         \
+        */                                                             \
+       barrier();                                                      \
+       ___ctx->cnt = 1;                                                \
+       /*                                                              \
+        * Make sure ___ctx->cnt is set before we save ___hcr. We       \
+        * don't want ___ctx->hcr to be overwritten.                    \
+        */                                                             \
+       barrier();                                                      \
+       ___ctx->hcr = ___hcr;                                           \
+} while (0)
 
-#define arch_nmi_exit()                                                                \
-       do {                                                                    \
-               if (is_kernel_in_hyp_mode()) {                                  \
-                       struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts);  \
-                       if (!(nmi_ctx->hcr & HCR_TGE))                          \
-                               write_sysreg(nmi_ctx->hcr, hcr_el2);            \
-               }                                                               \
-       } while (0)
+#define arch_nmi_exit()                                                        \
+do {                                                                   \
+       struct nmi_ctx *___ctx;                                         \
+       u64 ___hcr;                                                     \
+                                                                       \
+       if (!is_kernel_in_hyp_mode())                                   \
+               break;                                                  \
+                                                                       \
+       ___ctx = this_cpu_ptr(&nmi_contexts);                           \
+       ___hcr = ___ctx->hcr;                                           \
+       /*                                                              \
+        * Make sure we read ___ctx->hcr before we release              \
+        * ___ctx->cnt as it makes ___ctx->hcr updatable again.         \
+        */                                                             \
+       barrier();                                                      \
+       ___ctx->cnt--;                                                  \
+       /*                                                              \
+        * Make sure ___ctx->cnt release is visible before we           \
+        * restore the sysreg. Otherwise a new NMI occurring            \
+        * right after write_sysreg() can be fooled and think           \
+        * we secured things for it.                                    \
+        */                                                             \
+       barrier();                                                      \
+       if (!___ctx->cnt && !(___hcr & HCR_TGE))                        \
+               write_sysreg(___hcr, hcr_el2);                          \
+} while (0)
 
 static inline void ack_bad_irq(unsigned int irq)
 {
index 0f00265248b5c2d67fab6b9bae98f47dfab8d665..d683bcbf1e7c074fd007a794117a7c66ebe13ea1 100644 (file)
@@ -94,6 +94,7 @@
 #define KERNEL_HWCAP_BF16              __khwcap2_feature(BF16)
 #define KERNEL_HWCAP_DGH               __khwcap2_feature(DGH)
 #define KERNEL_HWCAP_RNG               __khwcap2_feature(RNG)
+#define KERNEL_HWCAP_BTI               __khwcap2_feature(BTI)
 
 /*
  * This yields a mask that user programs can use to figure out what
index bb313dde58a4b38a83a0589f3431d6805b1dca09..0bc46149e491751fea52be0de502157eb9d83143 100644 (file)
@@ -39,13 +39,37 @@ enum aarch64_insn_encoding_class {
                                         * system instructions */
 };
 
-enum aarch64_insn_hint_op {
+enum aarch64_insn_hint_cr_op {
        AARCH64_INSN_HINT_NOP   = 0x0 << 5,
        AARCH64_INSN_HINT_YIELD = 0x1 << 5,
        AARCH64_INSN_HINT_WFE   = 0x2 << 5,
        AARCH64_INSN_HINT_WFI   = 0x3 << 5,
        AARCH64_INSN_HINT_SEV   = 0x4 << 5,
        AARCH64_INSN_HINT_SEVL  = 0x5 << 5,
+
+       AARCH64_INSN_HINT_XPACLRI    = 0x07 << 5,
+       AARCH64_INSN_HINT_PACIA_1716 = 0x08 << 5,
+       AARCH64_INSN_HINT_PACIB_1716 = 0x0A << 5,
+       AARCH64_INSN_HINT_AUTIA_1716 = 0x0C << 5,
+       AARCH64_INSN_HINT_AUTIB_1716 = 0x0E << 5,
+       AARCH64_INSN_HINT_PACIAZ     = 0x18 << 5,
+       AARCH64_INSN_HINT_PACIASP    = 0x19 << 5,
+       AARCH64_INSN_HINT_PACIBZ     = 0x1A << 5,
+       AARCH64_INSN_HINT_PACIBSP    = 0x1B << 5,
+       AARCH64_INSN_HINT_AUTIAZ     = 0x1C << 5,
+       AARCH64_INSN_HINT_AUTIASP    = 0x1D << 5,
+       AARCH64_INSN_HINT_AUTIBZ     = 0x1E << 5,
+       AARCH64_INSN_HINT_AUTIBSP    = 0x1F << 5,
+
+       AARCH64_INSN_HINT_ESB  = 0x10 << 5,
+       AARCH64_INSN_HINT_PSB  = 0x11 << 5,
+       AARCH64_INSN_HINT_TSB  = 0x12 << 5,
+       AARCH64_INSN_HINT_CSDB = 0x14 << 5,
+
+       AARCH64_INSN_HINT_BTI   = 0x20 << 5,
+       AARCH64_INSN_HINT_BTIC  = 0x22 << 5,
+       AARCH64_INSN_HINT_BTIJ  = 0x24 << 5,
+       AARCH64_INSN_HINT_BTIJC = 0x26 << 5,
 };
 
 enum aarch64_insn_imm_type {
@@ -344,7 +368,7 @@ __AARCH64_INSN_FUNCS(msr_reg,       0xFFF00000, 0xD5100000)
 
 #undef __AARCH64_INSN_FUNCS
 
-bool aarch64_insn_is_nop(u32 insn);
+bool aarch64_insn_is_steppable_hint(u32 insn);
 bool aarch64_insn_is_branch_imm(u32 insn);
 
 static inline bool aarch64_insn_is_adr_adrp(u32 insn)
@@ -370,7 +394,7 @@ u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
                                     enum aarch64_insn_branch_type type);
 u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
                                     enum aarch64_insn_condition cond);
-u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op);
+u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_cr_op op);
 u32 aarch64_insn_gen_nop(void);
 u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg,
                                enum aarch64_insn_branch_type type);
index a30b4eec7cb40048c92d9d4261765446c2365af8..6ea53e6e8b262b1497de1e9cc4b0b4be493f04c3 100644 (file)
@@ -507,10 +507,12 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu,
 
 static __always_inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
 {
-       if (vcpu_mode_is_32bit(vcpu))
+       if (vcpu_mode_is_32bit(vcpu)) {
                kvm_skip_instr32(vcpu, is_wide_instr);
-       else
+       } else {
                *vcpu_pc(vcpu) += 4;
+               *vcpu_cpsr(vcpu) &= ~PSR_BTYPE_MASK;
+       }
 
        /* advance the singlestep state machine */
        *vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
index 32c8a675e5a4a33c89a12cdfd69d3d558b92f95f..57c0afcf9dcf7b658ceb0a73b0f8f365cd2f4b4a 100644 (file)
@@ -573,10 +573,6 @@ static inline bool kvm_arch_requires_vhe(void)
        if (system_supports_sve())
                return true;
 
-       /* Some implementations have defects that confine them to VHE */
-       if (cpus_have_cap(ARM64_WORKAROUND_SPECULATIVE_AT_VHE))
-               return true;
-
        return false;
 }
 
@@ -670,7 +666,7 @@ static inline int kvm_arm_have_ssbd(void)
 void kvm_vcpu_load_sysregs(struct kvm_vcpu *vcpu);
 void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu);
 
-void kvm_set_ipa_limit(void);
+int kvm_set_ipa_limit(void);
 
 #define __KVM_HAVE_ARCH_VM_ALLOC
 struct kvm *kvm_arch_alloc_vm(void);
index fe57f60f06a8944fff74b2e3f1ed5de6d1790626..015883671ec34c841bd2aca5fe8d0d1ceab6d5de 100644 (file)
 #include <linux/compiler.h>
 #include <linux/kvm_host.h>
 #include <asm/alternative.h>
-#include <asm/kvm_mmu.h>
 #include <asm/sysreg.h>
 
-#define __hyp_text __section(.hyp.text) notrace
+#define __hyp_text __section(.hyp.text) notrace __noscs
 
 #define read_sysreg_elx(r,nvh,vh)                                      \
        ({                                                              \
@@ -88,22 +87,5 @@ void deactivate_traps_vhe_put(void);
 u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt);
 void __noreturn __hyp_do_panic(unsigned long, ...);
 
-/*
- * Must be called from hyp code running at EL2 with an updated VTTBR
- * and interrupts disabled.
- */
-static __always_inline void __hyp_text __load_guest_stage2(struct kvm *kvm)
-{
-       write_sysreg(kvm->arch.vtcr, vtcr_el2);
-       write_sysreg(kvm_get_vttbr(kvm), vttbr_el2);
-
-       /*
-        * ARM errata 1165522 and 1530923 require the actual execution of the
-        * above before we can switch to the EL1/EL0 translation regime used by
-        * the guest.
-        */
-       asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT_VHE));
-}
-
 #endif /* __ARM64_KVM_HYP_H__ */
 
index 30b0e8d6b8953f03b6ef71e902b11daa2b11d858..85da6befe76e392a9933de4b9d6da17bdcb5de6a 100644 (file)
@@ -416,7 +416,7 @@ static inline unsigned int kvm_get_vmid_bits(void)
 {
        int reg = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
 
-       return (cpuid_feature_extract_unsigned_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
+       return get_vmid_bits(reg);
 }
 
 /*
@@ -604,5 +604,22 @@ static __always_inline u64 kvm_get_vttbr(struct kvm *kvm)
        return kvm_phys_to_vttbr(baddr) | vmid_field | cnp;
 }
 
+/*
+ * Must be called from hyp code running at EL2 with an updated VTTBR
+ * and interrupts disabled.
+ */
+static __always_inline void __load_guest_stage2(struct kvm *kvm)
+{
+       write_sysreg(kvm->arch.vtcr, vtcr_el2);
+       write_sysreg(kvm_get_vttbr(kvm), vttbr_el2);
+
+       /*
+        * ARM errata 1165522 and 1530923 require the actual execution of the
+        * above before we can switch to the EL1/EL0 translation regime used by
+        * the guest.
+        */
+       asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT));
+}
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ARM64_KVM_MMU_H__ */
index ebee3113a62ff7bbc17e12f8de318e9db37caac3..81fefd2a1d02300fd878f01e5517e61723c0fac1 100644 (file)
@@ -4,6 +4,52 @@
 #define __ALIGN                .align 2
 #define __ALIGN_STR    ".align 2"
 
+#if defined(CONFIG_ARM64_BTI_KERNEL) && defined(__aarch64__)
+
+/*
+ * Since current versions of gas reject the BTI instruction unless we
+ * set the architecture version to v8.5 we use the hint instruction
+ * instead.
+ */
+#define BTI_C hint 34 ;
+#define BTI_J hint 36 ;
+
+/*
+ * When using in-kernel BTI we need to ensure that PCS-conformant assembly
+ * functions have suitable annotations.  Override SYM_FUNC_START to insert
+ * a BTI landing pad at the start of everything.
+ */
+#define SYM_FUNC_START(name)                           \
+       SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)      \
+       BTI_C
+
+#define SYM_FUNC_START_NOALIGN(name)                   \
+       SYM_START(name, SYM_L_GLOBAL, SYM_A_NONE)       \
+       BTI_C
+
+#define SYM_FUNC_START_LOCAL(name)                     \
+       SYM_START(name, SYM_L_LOCAL, SYM_A_ALIGN)       \
+       BTI_C
+
+#define SYM_FUNC_START_LOCAL_NOALIGN(name)             \
+       SYM_START(name, SYM_L_LOCAL, SYM_A_NONE)        \
+       BTI_C
+
+#define SYM_FUNC_START_WEAK(name)                      \
+       SYM_START(name, SYM_L_WEAK, SYM_A_ALIGN)        \
+       BTI_C
+
+#define SYM_FUNC_START_WEAK_NOALIGN(name)              \
+       SYM_START(name, SYM_L_WEAK, SYM_A_NONE)         \
+       BTI_C
+
+#define SYM_INNER_LABEL(name, linkage)                 \
+       .type name SYM_T_NONE ASM_NL                    \
+       SYM_ENTRY(name, linkage, SYM_A_NONE)            \
+       BTI_J
+
+#endif
+
 /*
  * Annotate a function as position independent, i.e., safe to be called before
  * the kernel virtual mapping is activated.
diff --git a/arch/arm64/include/asm/mman.h b/arch/arm64/include/asm/mman.h
new file mode 100644 (file)
index 0000000..081ec8d
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MMAN_H__
+#define __ASM_MMAN_H__
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <uapi/asm/mman.h>
+
+static inline unsigned long arch_calc_vm_prot_bits(unsigned long prot,
+       unsigned long pkey __always_unused)
+{
+       if (system_supports_bti() && (prot & PROT_BTI))
+               return VM_ARM64_BTI;
+
+       return 0;
+}
+#define arch_calc_vm_prot_bits(prot, pkey) arch_calc_vm_prot_bits(prot, pkey)
+
+static inline pgprot_t arch_vm_get_page_prot(unsigned long vm_flags)
+{
+       return (vm_flags & VM_ARM64_BTI) ? __pgprot(PTE_GP) : __pgprot(0);
+}
+#define arch_vm_get_page_prot(vm_flags) arch_vm_get_page_prot(vm_flags)
+
+static inline bool arch_validate_prot(unsigned long prot,
+       unsigned long addr __always_unused)
+{
+       unsigned long supported = PROT_READ | PROT_WRITE | PROT_EXEC | PROT_SEM;
+
+       if (system_supports_bti())
+               supported |= PROT_BTI;
+
+       return (prot & ~supported) == 0;
+}
+#define arch_validate_prot(prot, addr) arch_validate_prot(prot, addr)
+
+#endif /* ! __ASM_MMAN_H__ */
index 6bf5e650da7883ba560f2f43a037d4f69facc3e4..9c91a8f93a0e9e00b2c47f5b329c6e858e39c14a 100644 (file)
 #define PTE_SHARED             (_AT(pteval_t, 3) << 8)         /* SH[1:0], inner shareable */
 #define PTE_AF                 (_AT(pteval_t, 1) << 10)        /* Access Flag */
 #define PTE_NG                 (_AT(pteval_t, 1) << 11)        /* nG */
+#define PTE_GP                 (_AT(pteval_t, 1) << 50)        /* BTI guarded */
 #define PTE_DBM                        (_AT(pteval_t, 1) << 51)        /* Dirty Bit Management */
 #define PTE_CONT               (_AT(pteval_t, 1) << 52)        /* Contiguous range */
 #define PTE_PXN                        (_AT(pteval_t, 1) << 53)        /* Privileged XN */
  * Memory Attribute override for Stage-2 (MemAttr[3:0])
  */
 #define PTE_S2_MEMATTR(t)      (_AT(pteval_t, (t)) << 2)
-#define PTE_S2_MEMATTR_MASK    (_AT(pteval_t, 0xf) << 2)
 
 /*
  * EL2/HYP PTE/PMD definitions
index 1305e28225fc77bb5c0a8a2bbd6ed8540934c835..2e7e0f452301820efc4ccf45730cd5d627778d65 100644 (file)
@@ -21,6 +21,7 @@
 
 #ifndef __ASSEMBLY__
 
+#include <asm/cpufeature.h>
 #include <asm/pgtable-types.h>
 
 extern bool arm64_use_ng_mappings;
@@ -31,6 +32,16 @@ extern bool arm64_use_ng_mappings;
 #define PTE_MAYBE_NG           (arm64_use_ng_mappings ? PTE_NG : 0)
 #define PMD_MAYBE_NG           (arm64_use_ng_mappings ? PMD_SECT_NG : 0)
 
+/*
+ * If we have userspace only BTI we don't want to mark kernel pages
+ * guarded even if the system does support BTI.
+ */
+#ifdef CONFIG_ARM64_BTI_KERNEL
+#define PTE_MAYBE_GP           (system_supports_bti() ? PTE_GP : 0)
+#else
+#define PTE_MAYBE_GP           0
+#endif
+
 #define PROT_DEFAULT           (_PROT_DEFAULT | PTE_MAYBE_NG)
 #define PROT_SECT_DEFAULT      (_PROT_SECT_DEFAULT | PMD_MAYBE_NG)
 
index 538c85e62f86d5fbd2f064a66278b917deab240e..dae0466d19d6ebd474160c6a4958c438e07e4616 100644 (file)
@@ -407,6 +407,9 @@ static inline pmd_t pmd_mkdevmap(pmd_t pmd)
 #define __pgprot_modify(prot,mask,bits) \
        __pgprot((pgprot_val(prot) & ~(mask)) | (bits))
 
+#define pgprot_nx(prot) \
+       __pgprot_modify(prot, 0, PTE_PXN)
+
 /*
  * Mark the prot value as uncacheable and unbufferable.
  */
@@ -457,6 +460,7 @@ extern pgd_t init_pg_dir[PTRS_PER_PGD];
 extern pgd_t init_pg_end[];
 extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
 extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
+extern pgd_t idmap_pg_end[];
 extern pgd_t tramp_pg_dir[PTRS_PER_PGD];
 
 extern void set_swapper_pgd(pgd_t *pgdp, pgd_t pgd);
@@ -508,7 +512,7 @@ static inline void pte_unmap(pte_t *pte) { }
 #define pte_set_fixmap_offset(pmd, addr)       pte_set_fixmap(pte_offset_phys(pmd, addr))
 #define pte_clear_fixmap()             clear_fixmap(FIX_PTE)
 
-#define pmd_page(pmd)          pfn_to_page(__phys_to_pfn(__pmd_to_phys(pmd)))
+#define pmd_page(pmd)                  phys_to_page(__pmd_to_phys(pmd))
 
 /* use ONLY for statically allocated translation tables */
 #define pte_offset_kimg(dir,addr)      ((pte_t *)__phys_to_kimg(pte_offset_phys((dir), (addr))))
@@ -566,7 +570,7 @@ static inline phys_addr_t pud_page_paddr(pud_t pud)
 #define pmd_set_fixmap_offset(pud, addr)       pmd_set_fixmap(pmd_offset_phys(pud, addr))
 #define pmd_clear_fixmap()             clear_fixmap(FIX_PMD)
 
-#define pud_page(pud)          pfn_to_page(__phys_to_pfn(__pud_to_phys(pud)))
+#define pud_page(pud)                  phys_to_page(__pud_to_phys(pud))
 
 /* use ONLY for statically allocated translation tables */
 #define pmd_offset_kimg(dir,addr)      ((pmd_t *)__phys_to_kimg(pmd_offset_phys((dir), (addr))))
@@ -624,7 +628,7 @@ static inline phys_addr_t pgd_page_paddr(pgd_t pgd)
 #define pud_set_fixmap_offset(pgd, addr)       pud_set_fixmap(pud_offset_phys(pgd, addr))
 #define pud_clear_fixmap()             clear_fixmap(FIX_PUD)
 
-#define pgd_page(pgd)          pfn_to_page(__phys_to_pfn(__pgd_to_phys(pgd)))
+#define pgd_page(pgd)                  phys_to_page(__pgd_to_phys(pgd))
 
 /* use ONLY for statically allocated translation tables */
 #define pud_offset_kimg(dir,addr)      ((pud_t *)__phys_to_kimg(pud_offset_phys((dir), (addr))))
@@ -660,7 +664,7 @@ static inline phys_addr_t pgd_page_paddr(pgd_t pgd)
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
        const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY |
-                             PTE_PROT_NONE | PTE_VALID | PTE_WRITE;
+                             PTE_PROT_NONE | PTE_VALID | PTE_WRITE | PTE_GP;
        /* preserve the hardware dirty information */
        if (pte_hw_dirty(pte))
                pte = pte_mkdirty(pte);
index bf57308fcd635e0451bdc716c42646518ac72743..2172ec7594ba5435e7dfef1ebd45f629ef87e3b9 100644 (file)
@@ -35,6 +35,7 @@
 #define GIC_PRIO_PSR_I_SET             (1 << 4)
 
 /* Additional SPSR bits not exposed in the UABI */
+
 #define PSR_IL_BIT             (1 << 20)
 
 /* AArch32-specific ptrace requests */
diff --git a/arch/arm64/include/asm/scs.h b/arch/arm64/include/asm/scs.h
new file mode 100644 (file)
index 0000000..eaa2cd9
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_SCS_H
+#define _ASM_SCS_H
+
+#ifdef __ASSEMBLY__
+
+#include <asm/asm-offsets.h>
+
+#ifdef CONFIG_SHADOW_CALL_STACK
+       scs_sp  .req    x18
+
+       .macro scs_load tsk, tmp
+       ldr     scs_sp, [\tsk, #TSK_TI_SCS_SP]
+       .endm
+
+       .macro scs_save tsk, tmp
+       str     scs_sp, [\tsk, #TSK_TI_SCS_SP]
+       .endm
+#else
+       .macro scs_load tsk, tmp
+       .endm
+
+       .macro scs_save tsk, tmp
+       .endm
+#endif /* CONFIG_SHADOW_CALL_STACK */
+
+#endif /* __ASSEMBLY __ */
+
+#endif /* _ASM_SCS_H */
index 40d5ba029615f63ff53a65ab9e1fbe2425a5d359..ea268d88b6f7e6e5318c8c9671dedb740b0d6dc1 100644 (file)
 #define CPU_STUCK_REASON_52_BIT_VA     (UL(1) << CPU_STUCK_REASON_SHIFT)
 #define CPU_STUCK_REASON_NO_GRAN       (UL(2) << CPU_STUCK_REASON_SHIFT)
 
-/* Possible options for __cpu_setup */
-/* Option to setup primary cpu */
-#define ARM64_CPU_BOOT_PRIMARY         (1)
-/* Option to setup secondary cpus */
-#define ARM64_CPU_BOOT_SECONDARY       (2)
-/* Option to setup cpus for different cpu run time services */
-#define ARM64_CPU_RUNTIME              (3)
-
 #ifndef __ASSEMBLY__
 
 #include <asm/percpu.h>
@@ -96,9 +88,6 @@ asmlinkage void secondary_start_kernel(void);
 struct secondary_data {
        void *stack;
        struct task_struct *task;
-#ifdef CONFIG_ARM64_PTR_AUTH
-       struct ptrauth_keys_kernel ptrauth_key;
-#endif
        long status;
 };
 
index 4d9b1f48dc39e800ac645eb14443367708c8d10e..5017b531a41531d384a97046e0bc0efedb21abad 100644 (file)
@@ -68,12 +68,10 @@ extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk);
 
 DECLARE_PER_CPU(unsigned long *, irq_stack_ptr);
 
-static inline bool on_irq_stack(unsigned long sp,
+static inline bool on_stack(unsigned long sp, unsigned long low,
+                               unsigned long high, enum stack_type type,
                                struct stack_info *info)
 {
-       unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
-       unsigned long high = low + IRQ_STACK_SIZE;
-
        if (!low)
                return false;
 
@@ -83,12 +81,20 @@ static inline bool on_irq_stack(unsigned long sp,
        if (info) {
                info->low = low;
                info->high = high;
-               info->type = STACK_TYPE_IRQ;
+               info->type = type;
        }
-
        return true;
 }
 
+static inline bool on_irq_stack(unsigned long sp,
+                               struct stack_info *info)
+{
+       unsigned long low = (unsigned long)raw_cpu_read(irq_stack_ptr);
+       unsigned long high = low + IRQ_STACK_SIZE;
+
+       return on_stack(sp, low, high, STACK_TYPE_IRQ, info);
+}
+
 static inline bool on_task_stack(const struct task_struct *tsk,
                                 unsigned long sp,
                                 struct stack_info *info)
@@ -96,16 +102,7 @@ static inline bool on_task_stack(const struct task_struct *tsk,
        unsigned long low = (unsigned long)task_stack_page(tsk);
        unsigned long high = low + THREAD_SIZE;
 
-       if (sp < low || sp >= high)
-               return false;
-
-       if (info) {
-               info->low = low;
-               info->high = high;
-               info->type = STACK_TYPE_TASK;
-       }
-
-       return true;
+       return on_stack(sp, low, high, STACK_TYPE_TASK, info);
 }
 
 #ifdef CONFIG_VMAP_STACK
@@ -117,16 +114,7 @@ static inline bool on_overflow_stack(unsigned long sp,
        unsigned long low = (unsigned long)raw_cpu_ptr(overflow_stack);
        unsigned long high = low + OVERFLOW_STACK_SIZE;
 
-       if (sp < low || sp >= high)
-               return false;
-
-       if (info) {
-               info->low = low;
-               info->high = high;
-               info->type = STACK_TYPE_OVERFLOW;
-       }
-
-       return true;
+       return on_stack(sp, low, high, STACK_TYPE_OVERFLOW, info);
 }
 #else
 static inline bool on_overflow_stack(unsigned long sp,
index 8939c87c4dcef4e93b62e35e30f0b7a5a47bc197..0cde2f473971e87cd355ce2e56451c8213fcd02a 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef __ASM_SUSPEND_H
 #define __ASM_SUSPEND_H
 
-#define NR_CTX_REGS 12
+#define NR_CTX_REGS 13
 #define NR_CALLEE_SAVED_REGS 12
 
 /*
index c4ac0ac25a00809bb36d45adcfcd00e5ff369eca..463175f80341f98b7ae4ad7bfe78d60b55ea5d59 100644 (file)
 #define SYS_DC_CSW                     sys_insn(1, 0, 7, 10, 2)
 #define SYS_DC_CISW                    sys_insn(1, 0, 7, 14, 2)
 
+/*
+ * System registers, organised loosely by encoding but grouped together
+ * where the architected name contains an index. e.g. ID_MMFR<n>_EL1.
+ */
 #define SYS_OSDTRRX_EL1                        sys_reg(2, 0, 0, 0, 2)
 #define SYS_MDCCINT_EL1                        sys_reg(2, 0, 0, 2, 0)
 #define SYS_MDSCR_EL1                  sys_reg(2, 0, 0, 2, 2)
 
 #define SYS_ID_PFR0_EL1                        sys_reg(3, 0, 0, 1, 0)
 #define SYS_ID_PFR1_EL1                        sys_reg(3, 0, 0, 1, 1)
+#define SYS_ID_PFR2_EL1                        sys_reg(3, 0, 0, 3, 4)
 #define SYS_ID_DFR0_EL1                        sys_reg(3, 0, 0, 1, 2)
+#define SYS_ID_DFR1_EL1                        sys_reg(3, 0, 0, 3, 5)
 #define SYS_ID_AFR0_EL1                        sys_reg(3, 0, 0, 1, 3)
 #define SYS_ID_MMFR0_EL1               sys_reg(3, 0, 0, 1, 4)
 #define SYS_ID_MMFR1_EL1               sys_reg(3, 0, 0, 1, 5)
 #define SYS_ID_MMFR2_EL1               sys_reg(3, 0, 0, 1, 6)
 #define SYS_ID_MMFR3_EL1               sys_reg(3, 0, 0, 1, 7)
+#define SYS_ID_MMFR4_EL1               sys_reg(3, 0, 0, 2, 6)
+#define SYS_ID_MMFR5_EL1               sys_reg(3, 0, 0, 3, 6)
 
 #define SYS_ID_ISAR0_EL1               sys_reg(3, 0, 0, 2, 0)
 #define SYS_ID_ISAR1_EL1               sys_reg(3, 0, 0, 2, 1)
 #define SYS_ID_ISAR3_EL1               sys_reg(3, 0, 0, 2, 3)
 #define SYS_ID_ISAR4_EL1               sys_reg(3, 0, 0, 2, 4)
 #define SYS_ID_ISAR5_EL1               sys_reg(3, 0, 0, 2, 5)
-#define SYS_ID_MMFR4_EL1               sys_reg(3, 0, 0, 2, 6)
 #define SYS_ID_ISAR6_EL1               sys_reg(3, 0, 0, 2, 7)
 
 #define SYS_MVFR0_EL1                  sys_reg(3, 0, 0, 3, 0)
 #endif
 
 /* SCTLR_EL1 specific flags. */
+#define SCTLR_EL1_BT1          (BIT(36))
+#define SCTLR_EL1_BT0          (BIT(35))
 #define SCTLR_EL1_UCI          (BIT(26))
 #define SCTLR_EL1_E0E          (BIT(24))
 #define SCTLR_EL1_SPAN         (BIT(23))
 
 /* id_aa64isar0 */
 #define ID_AA64ISAR0_RNDR_SHIFT                60
+#define ID_AA64ISAR0_TLB_SHIFT         56
 #define ID_AA64ISAR0_TS_SHIFT          52
 #define ID_AA64ISAR0_FHM_SHIFT         48
 #define ID_AA64ISAR0_DP_SHIFT          44
 #define ID_AA64PFR0_CSV2_SHIFT         56
 #define ID_AA64PFR0_DIT_SHIFT          48
 #define ID_AA64PFR0_AMU_SHIFT          44
+#define ID_AA64PFR0_MPAM_SHIFT         40
+#define ID_AA64PFR0_SEL2_SHIFT         36
 #define ID_AA64PFR0_SVE_SHIFT          32
 #define ID_AA64PFR0_RAS_SHIFT          28
 #define ID_AA64PFR0_GIC_SHIFT          24
 #define ID_AA64PFR0_ASIMD_NI           0xf
 #define ID_AA64PFR0_ASIMD_SUPPORTED    0x0
 #define ID_AA64PFR0_EL1_64BIT_ONLY     0x1
+#define ID_AA64PFR0_EL1_32BIT_64BIT    0x2
 #define ID_AA64PFR0_EL0_64BIT_ONLY     0x1
 #define ID_AA64PFR0_EL0_32BIT_64BIT    0x2
 
 /* id_aa64pfr1 */
+#define ID_AA64PFR1_MPAMFRAC_SHIFT     16
+#define ID_AA64PFR1_RASFRAC_SHIFT      12
+#define ID_AA64PFR1_MTE_SHIFT          8
 #define ID_AA64PFR1_SSBS_SHIFT         4
+#define ID_AA64PFR1_BT_SHIFT           0
 
 #define ID_AA64PFR1_SSBS_PSTATE_NI     0
 #define ID_AA64PFR1_SSBS_PSTATE_ONLY   1
 #define ID_AA64PFR1_SSBS_PSTATE_INSNS  2
+#define ID_AA64PFR1_BT_BTI             0x1
 
 /* id_aa64zfr0 */
 #define ID_AA64ZFR0_F64MM_SHIFT                56
 #define ID_AA64ZFR0_SVEVER_SVE2                0x1
 
 /* id_aa64mmfr0 */
+#define ID_AA64MMFR0_TGRAN4_2_SHIFT    40
+#define ID_AA64MMFR0_TGRAN64_2_SHIFT   36
+#define ID_AA64MMFR0_TGRAN16_2_SHIFT   32
 #define ID_AA64MMFR0_TGRAN4_SHIFT      28
 #define ID_AA64MMFR0_TGRAN64_SHIFT     24
 #define ID_AA64MMFR0_TGRAN16_SHIFT     20
 
 #define ID_DFR0_PERFMON_8_1            0x4
 
+#define ID_ISAR4_SWP_FRAC_SHIFT                28
+#define ID_ISAR4_PSR_M_SHIFT           24
+#define ID_ISAR4_SYNCH_PRIM_FRAC_SHIFT 20
+#define ID_ISAR4_BARRIER_SHIFT         16
+#define ID_ISAR4_SMC_SHIFT             12
+#define ID_ISAR4_WRITEBACK_SHIFT       8
+#define ID_ISAR4_WITHSHIFTS_SHIFT      4
+#define ID_ISAR4_UNPRIV_SHIFT          0
+
+#define ID_DFR1_MTPMU_SHIFT            0
+
+#define ID_ISAR0_DIVIDE_SHIFT          24
+#define ID_ISAR0_DEBUG_SHIFT           20
+#define ID_ISAR0_COPROC_SHIFT          16
+#define ID_ISAR0_CMPBRANCH_SHIFT       12
+#define ID_ISAR0_BITFIELD_SHIFT                8
+#define ID_ISAR0_BITCOUNT_SHIFT                4
+#define ID_ISAR0_SWAP_SHIFT            0
+
 #define ID_ISAR5_RDM_SHIFT             24
 #define ID_ISAR5_CRC32_SHIFT           16
 #define ID_ISAR5_SHA2_SHIFT            12
 #define ID_ISAR6_DP_SHIFT              4
 #define ID_ISAR6_JSCVT_SHIFT           0
 
+#define ID_MMFR4_EVT_SHIFT             28
+#define ID_MMFR4_CCIDX_SHIFT           24
+#define ID_MMFR4_LSM_SHIFT             20
+#define ID_MMFR4_HPDS_SHIFT            16
+#define ID_MMFR4_CNP_SHIFT             12
+#define ID_MMFR4_XNX_SHIFT             8
+#define ID_MMFR4_SPECSEI_SHIFT         0
+
+#define ID_MMFR5_ETS_SHIFT             0
+
+#define ID_PFR0_DIT_SHIFT              24
+#define ID_PFR0_CSV2_SHIFT             16
+
+#define ID_PFR2_SSBS_SHIFT             4
+#define ID_PFR2_CSV3_SHIFT             0
+
 #define MVFR0_FPROUND_SHIFT            28
 #define MVFR0_FPSHVEC_SHIFT            24
 #define MVFR0_FPSQRT_SHIFT             20
 #define MVFR1_FPDNAN_SHIFT             4
 #define MVFR1_FPFTZ_SHIFT              0
 
-
-#define ID_AA64MMFR0_TGRAN4_SHIFT      28
-#define ID_AA64MMFR0_TGRAN64_SHIFT     24
-#define ID_AA64MMFR0_TGRAN16_SHIFT     20
-
-#define ID_AA64MMFR0_TGRAN4_NI         0xf
-#define ID_AA64MMFR0_TGRAN4_SUPPORTED  0x0
-#define ID_AA64MMFR0_TGRAN64_NI                0xf
-#define ID_AA64MMFR0_TGRAN64_SUPPORTED 0x0
-#define ID_AA64MMFR0_TGRAN16_NI                0x0
-#define ID_AA64MMFR0_TGRAN16_SUPPORTED 0x1
+#define ID_PFR1_GIC_SHIFT              28
+#define ID_PFR1_VIRT_FRAC_SHIFT                24
+#define ID_PFR1_SEC_FRAC_SHIFT         20
+#define ID_PFR1_GENTIMER_SHIFT         16
+#define ID_PFR1_VIRTUALIZATION_SHIFT   12
+#define ID_PFR1_MPROGMOD_SHIFT         8
+#define ID_PFR1_SECURITY_SHIFT         4
+#define ID_PFR1_PROGMOD_SHIFT          0
 
 #if defined(CONFIG_ARM64_4K_PAGES)
 #define ID_AA64MMFR0_TGRAN_SHIFT       ID_AA64MMFR0_TGRAN4_SHIFT
index 512174a8e7891527ef071d9dd275723ec3930378..6ea8b6a26ae9b956bbdeafb36203e63022092867 100644 (file)
@@ -41,6 +41,10 @@ struct thread_info {
 #endif
                } preempt;
        };
+#ifdef CONFIG_SHADOW_CALL_STACK
+       void                    *scs_base;
+       void                    *scs_sp;
+#endif
 };
 
 #define thread_saved_pc(tsk)   \
@@ -100,11 +104,20 @@ void arch_release_task_struct(struct task_struct *tsk);
                                 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
                                 _TIF_SYSCALL_EMU)
 
+#ifdef CONFIG_SHADOW_CALL_STACK
+#define INIT_SCS                                                       \
+       .scs_base       = init_shadow_call_stack,                       \
+       .scs_sp         = init_shadow_call_stack,
+#else
+#define INIT_SCS
+#endif
+
 #define INIT_THREAD_INFO(tsk)                                          \
 {                                                                      \
        .flags          = _TIF_FOREIGN_FPSTATE,                         \
        .preempt_count  = INIT_PREEMPT_COUNT,                           \
        .addr_limit     = KERNEL_DS,                                    \
+       INIT_SCS                                                        \
 }
 
 #endif /* __ASM_THREAD_INFO_H */
index 803039d504de609b188e62abd2fc102f00d73db5..3b859596840de5238645308ecd1bf389401822c5 100644 (file)
@@ -38,7 +38,7 @@
 #define __ARM_NR_compat_set_tls                (__ARM_NR_COMPAT_BASE + 5)
 #define __ARM_NR_COMPAT_END            (__ARM_NR_COMPAT_BASE + 0x800)
 
-#define __NR_compat_syscalls           439
+#define __NR_compat_syscalls           440
 #endif
 
 #define __ARCH_WANT_SYS_CLONE
index c1c61635f89c374c80cab2410e87fbe261209540..6d95d0c8bf2f47f29d028a2d3c4754d6a3c7a4e3 100644 (file)
@@ -883,6 +883,8 @@ __SYSCALL(__NR_clone3, sys_clone3)
 __SYSCALL(__NR_openat2, sys_openat2)
 #define __NR_pidfd_getfd 438
 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
+#define __NR_faccessat2 439
+__SYSCALL(__NR_faccessat2, sys_faccessat2)
 
 /*
  * Please add new compat syscalls above this comment and update
index 0a12115d96384f94b7f7b7902d490137bbe9c4e4..0cc6636e3f150e92332d53be00f2b8a0ba56df0e 100644 (file)
@@ -19,10 +19,8 @@ static inline unsigned long *arch_alloc_vmap_stack(size_t stack_size, int node)
 {
        BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK));
 
-       return __vmalloc_node_range(stack_size, THREAD_ALIGN,
-                                   VMALLOC_START, VMALLOC_END,
-                                   THREADINFO_GFP, PAGE_KERNEL, 0, node,
-                                   __builtin_return_address(0));
+       return __vmalloc_node(stack_size, THREAD_ALIGN, THREADINFO_GFP, node,
+                       __builtin_return_address(0));
 }
 
 #endif /* __ASM_VMAP_STACK_H */
index 7752d93bb50fa486d8e6df27049775e1d61572e8..2d6ba1c2592ed1d825a7a51fd645fd62f5a25647 100644 (file)
@@ -73,5 +73,6 @@
 #define HWCAP2_BF16            (1 << 14)
 #define HWCAP2_DGH             (1 << 15)
 #define HWCAP2_RNG             (1 << 16)
+#define HWCAP2_BTI             (1 << 17)
 
 #endif /* _UAPI__ASM_HWCAP_H */
diff --git a/arch/arm64/include/uapi/asm/mman.h b/arch/arm64/include/uapi/asm/mman.h
new file mode 100644 (file)
index 0000000..6fdd71e
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__ASM_MMAN_H
+#define _UAPI__ASM_MMAN_H
+
+#include <asm-generic/mman.h>
+
+#define PROT_BTI       0x10            /* BTI guarded page */
+
+#endif /* ! _UAPI__ASM_MMAN_H */
index d1bb5b69f1ce49578823fc84c588a8b86ac04c20..42cbe34d95ceeb3d9ff2d4fed0f20d0726586cea 100644 (file)
@@ -46,6 +46,7 @@
 #define PSR_I_BIT      0x00000080
 #define PSR_A_BIT      0x00000100
 #define PSR_D_BIT      0x00000200
+#define PSR_BTYPE_MASK 0x00000c00
 #define PSR_SSBS_BIT   0x00001000
 #define PSR_PAN_BIT    0x00400000
 #define PSR_UAO_BIT    0x00800000
@@ -55,6 +56,8 @@
 #define PSR_Z_BIT      0x40000000
 #define PSR_N_BIT      0x80000000
 
+#define PSR_BTYPE_SHIFT                10
+
 /*
  * Groups of PSR bits
  */
 #define PSR_x          0x0000ff00      /* Extension            */
 #define PSR_c          0x000000ff      /* Control              */
 
+/* Convenience names for the values of PSTATE.BTYPE */
+#define PSR_BTYPE_NONE         (0b00 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_JC           (0b01 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_C            (0b10 << PSR_BTYPE_SHIFT)
+#define PSR_BTYPE_J            (0b11 << PSR_BTYPE_SHIFT)
+
 /* syscall emulation path in ptrace */
 #define PTRACE_SYSEMU            31
 #define PTRACE_SYSEMU_SINGLESTEP  32
index 4e5b8ee314423ac9404c3c35ad6d7632cb9964e6..151f28521f1ece843ca9ac74bf92811354da090f 100644 (file)
@@ -63,6 +63,7 @@ obj-$(CONFIG_CRASH_CORE)              += crash_core.o
 obj-$(CONFIG_ARM_SDE_INTERFACE)                += sdei.o
 obj-$(CONFIG_ARM64_SSBD)               += ssbd.o
 obj-$(CONFIG_ARM64_PTR_AUTH)           += pointer_auth.o
+obj-$(CONFIG_SHADOW_CALL_STACK)                += scs.o
 
 obj-y                                  += vdso/ probes/
 obj-$(CONFIG_COMPAT_VDSO)              += vdso32/
index 9981a0a5a87f13b86067520f8ae1cf23a2a6fd9a..3539d7092612760cba39c071e5b3f4417fc6e51f 100644 (file)
@@ -33,6 +33,10 @@ int main(void)
   DEFINE(TSK_TI_ADDR_LIMIT,    offsetof(struct task_struct, thread_info.addr_limit));
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
   DEFINE(TSK_TI_TTBR0,         offsetof(struct task_struct, thread_info.ttbr0));
+#endif
+#ifdef CONFIG_SHADOW_CALL_STACK
+  DEFINE(TSK_TI_SCS_BASE,      offsetof(struct task_struct, thread_info.scs_base));
+  DEFINE(TSK_TI_SCS_SP,                offsetof(struct task_struct, thread_info.scs_sp));
 #endif
   DEFINE(TSK_STACK,            offsetof(struct task_struct, stack));
 #ifdef CONFIG_STACKPROTECTOR
@@ -92,9 +96,6 @@ int main(void)
   BLANK();
   DEFINE(CPU_BOOT_STACK,       offsetof(struct secondary_data, stack));
   DEFINE(CPU_BOOT_TASK,                offsetof(struct secondary_data, task));
-#ifdef CONFIG_ARM64_PTR_AUTH
-  DEFINE(CPU_BOOT_PTRAUTH_KEY, offsetof(struct secondary_data, ptrauth_key));
-#endif
   BLANK();
 #ifdef CONFIG_KVM_ARM_HOST
   DEFINE(VCPU_CONTEXT,         offsetof(struct kvm_vcpu, arch.ctxt));
index 38087b4c04322414d59e9649bef7ec0bef71230b..4a18055b2ff9d14bde58d4595107344829d2e2ce 100644 (file)
@@ -29,7 +29,7 @@
  * branch to what would be the reset vector. It must be executed with the
  * flat identity mapping.
  */
-ENTRY(__cpu_soft_restart)
+SYM_CODE_START(__cpu_soft_restart)
        /* Clear sctlr_el1 flags. */
        mrs     x12, sctlr_el1
        mov_q   x13, SCTLR_ELx_FLAGS
@@ -47,6 +47,6 @@ ENTRY(__cpu_soft_restart)
        mov     x1, x3                          // arg1
        mov     x2, x4                          // arg2
        br      x8
-ENDPROC(__cpu_soft_restart)
+SYM_CODE_END(__cpu_soft_restart)
 
 .popsection
index df56d2295d165a0d7bfa44c76cda0d3f2f53d6d3..b0ce6bf14f6a92c8746b008e9f3481e4ee0c162f 100644 (file)
@@ -635,7 +635,7 @@ has_neoverse_n1_erratum_1542419(const struct arm64_cpu_capabilities *entry,
        return is_midr_in_range(midr, &range) && has_dic;
 }
 
-#if defined(CONFIG_HARDEN_EL2_VECTORS) || defined(CONFIG_ARM64_ERRATUM_1319367)
+#if defined(CONFIG_HARDEN_EL2_VECTORS)
 
 static const struct midr_range ca57_a72[] = {
        MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
@@ -757,12 +757,16 @@ static const struct arm64_cpu_capabilities erratum_843419_list[] = {
 };
 #endif
 
-#ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT_VHE
-static const struct midr_range erratum_speculative_at_vhe_list[] = {
+#ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT
+static const struct midr_range erratum_speculative_at_list[] = {
 #ifdef CONFIG_ARM64_ERRATUM_1165522
        /* Cortex A76 r0p0 to r2p0 */
        MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 2, 0),
 #endif
+#ifdef CONFIG_ARM64_ERRATUM_1319367
+       MIDR_ALL_VERSIONS(MIDR_CORTEX_A57),
+       MIDR_ALL_VERSIONS(MIDR_CORTEX_A72),
+#endif
 #ifdef CONFIG_ARM64_ERRATUM_1530923
        /* Cortex A55 r0p0 to r2p0 */
        MIDR_RANGE(MIDR_CORTEX_A55, 0, 0, 2, 0),
@@ -774,7 +778,7 @@ static const struct midr_range erratum_speculative_at_vhe_list[] = {
 const struct arm64_cpu_capabilities arm64_errata[] = {
 #ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE
        {
-               .desc = "ARM errata 826319, 827319, 824069, 819472",
+               .desc = "ARM errata 826319, 827319, 824069, or 819472",
                .capability = ARM64_WORKAROUND_CLEAN_CACHE,
                ERRATA_MIDR_RANGE_LIST(workaround_clean_cache),
                .cpu_enable = cpu_enable_cache_maint_trap,
@@ -856,7 +860,7 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
 #endif
 #ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI
        {
-               .desc = "Qualcomm erratum 1009, ARM erratum 1286807",
+               .desc = "Qualcomm erratum 1009, or ARM erratum 1286807",
                .capability = ARM64_WORKAROUND_REPEAT_TLBI,
                .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
                .matches = cpucap_multi_entry_cap_matches,
@@ -897,11 +901,11 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                ERRATA_MIDR_RANGE_LIST(erratum_1418040_list),
        },
 #endif
-#ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT_VHE
+#ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT
        {
-               .desc = "ARM errata 1165522, 1530923",
-               .capability = ARM64_WORKAROUND_SPECULATIVE_AT_VHE,
-               ERRATA_MIDR_RANGE_LIST(erratum_speculative_at_vhe_list),
+               .desc = "ARM errata 1165522, 1319367, or 1530923",
+               .capability = ARM64_WORKAROUND_SPECULATIVE_AT,
+               ERRATA_MIDR_RANGE_LIST(erratum_speculative_at_list),
        },
 #endif
 #ifdef CONFIG_ARM64_ERRATUM_1463225
@@ -934,13 +938,6 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                .matches = has_neoverse_n1_erratum_1542419,
                .cpu_enable = cpu_enable_trap_ctr_access,
        },
-#endif
-#ifdef CONFIG_ARM64_ERRATUM_1319367
-       {
-               .desc = "ARM erratum 1319367",
-               .capability = ARM64_WORKAROUND_SPECULATIVE_AT_NVHE,
-               ERRATA_MIDR_RANGE_LIST(ca57_a72),
-       },
 #endif
        {
        }
index 9fac745aa7bb248771bf113c7b3e8539707af51a..4ae41670c2e6be9d5c0507e305d9b6a123aecd2e 100644 (file)
@@ -3,6 +3,61 @@
  * Contains CPU feature definitions
  *
  * Copyright (C) 2015 ARM Ltd.
+ *
+ * A note for the weary kernel hacker: the code here is confusing and hard to
+ * follow! That's partly because it's solving a nasty problem, but also because
+ * there's a little bit of over-abstraction that tends to obscure what's going
+ * on behind a maze of helper functions and macros.
+ *
+ * The basic problem is that hardware folks have started gluing together CPUs
+ * with distinct architectural features; in some cases even creating SoCs where
+ * user-visible instructions are available only on a subset of the available
+ * cores. We try to address this by snapshotting the feature registers of the
+ * boot CPU and comparing these with the feature registers of each secondary
+ * CPU when bringing them up. If there is a mismatch, then we update the
+ * snapshot state to indicate the lowest-common denominator of the feature,
+ * known as the "safe" value. This snapshot state can be queried to view the
+ * "sanitised" value of a feature register.
+ *
+ * The sanitised register values are used to decide which capabilities we
+ * have in the system. These may be in the form of traditional "hwcaps"
+ * advertised to userspace or internal "cpucaps" which are used to configure
+ * things like alternative patching and static keys. While a feature mismatch
+ * may result in a TAINT_CPU_OUT_OF_SPEC kernel taint, a capability mismatch
+ * may prevent a CPU from being onlined at all.
+ *
+ * Some implementation details worth remembering:
+ *
+ * - Mismatched features are *always* sanitised to a "safe" value, which
+ *   usually indicates that the feature is not supported.
+ *
+ * - A mismatched feature marked with FTR_STRICT will cause a "SANITY CHECK"
+ *   warning when onlining an offending CPU and the kernel will be tainted
+ *   with TAINT_CPU_OUT_OF_SPEC.
+ *
+ * - Features marked as FTR_VISIBLE have their sanitised value visible to
+ *   userspace. FTR_VISIBLE features in registers that are only visible
+ *   to EL0 by trapping *must* have a corresponding HWCAP so that late
+ *   onlining of CPUs cannot lead to features disappearing at runtime.
+ *
+ * - A "feature" is typically a 4-bit register field. A "capability" is the
+ *   high-level description derived from the sanitised field value.
+ *
+ * - Read the Arm ARM (DDI 0487F.a) section D13.1.3 ("Principles of the ID
+ *   scheme for fields in ID registers") to understand when feature fields
+ *   may be signed or unsigned (FTR_SIGNED and FTR_UNSIGNED accordingly).
+ *
+ * - KVM exposes its own view of the feature registers to guest operating
+ *   systems regardless of FTR_VISIBLE. This is typically driven from the
+ *   sanitised register values to allow virtual CPUs to be migrated between
+ *   arbitrary physical CPUs, but some features not present on the host are
+ *   also advertised and emulated. Look at sys_reg_descs[] for the gory
+ *   details.
+ *
+ * - If the arm64_ftr_bits[] for a register has a missing field, then this
+ *   field is treated as STRICT RES0, including for read_sanitised_ftr_reg().
+ *   This is stronger than FTR_HIDDEN and can be used to hide features from
+ *   KVM guests.
  */
 
 #define pr_fmt(fmt) "CPU features: " fmt
@@ -124,6 +179,7 @@ static bool __system_matches_cap(unsigned int n);
  */
 static const struct arm64_ftr_bits ftr_id_aa64isar0[] = {
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_RNDR_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_TLB_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_TS_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_FHM_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_DP_SHIFT, 4, 0),
@@ -166,22 +222,27 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV2_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_DIT_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_AMU_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_MPAM_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_SEL2_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_SVE),
                                   FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_SVE_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_RAS_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_GIC_SHIFT, 4, 0),
        S_ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI),
        S_ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI),
-       /* Linux doesn't care about the EL3 */
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL3_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL2_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL2_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
        ARM64_FTR_END,
 };
 
 static const struct arm64_ftr_bits ftr_id_aa64pfr1[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_MPAMFRAC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_RASFRAC_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_SSBS_SHIFT, 4, ID_AA64PFR1_SSBS_PSTATE_NI),
+       ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_BTI),
+                                   FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR1_BT_SHIFT, 4, 0),
        ARM64_FTR_END,
 };
 
@@ -208,6 +269,24 @@ static const struct arm64_ftr_bits ftr_id_aa64zfr0[] = {
 };
 
 static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
+       /*
+        * Page size not being supported at Stage-2 is not fatal. You
+        * just give up KVM if PAGE_SIZE isn't supported there. Go fix
+        * your favourite nesting hypervisor.
+        *
+        * There is a small corner case where the hypervisor explicitly
+        * advertises a given granule size at Stage-2 (value 2) on some
+        * vCPUs, and uses the fallback to Stage-1 (value 0) for other
+        * vCPUs. Although this is not forbidden by the architecture, it
+        * indicates that the hypervisor is being silly (or buggy).
+        *
+        * We make no effort to cope with this and pretend that if these
+        * fields are inconsistent across vCPUs, then it isn't worth
+        * trying to bring KVM up.
+        */
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN4_2_SHIFT, 4, 1),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN64_2_SHIFT, 4, 1),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN16_2_SHIFT, 4, 1),
        /*
         * We already refuse to boot CPUs that don't support our configured
         * page size, so we can only detect mismatches for a page size other
@@ -247,7 +326,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_FWB_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_AT_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_LVA_SHIFT, 4, 0),
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_IESB_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_IESB_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_LSM_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_UAO_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_CNP_SHIFT, 4, 0),
@@ -289,7 +368,7 @@ static const struct arm64_ftr_bits ftr_id_mmfr0[] = {
 };
 
 static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 36, 28, 0),
+       S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 36, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMSVER_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
@@ -316,6 +395,16 @@ static const struct arm64_ftr_bits ftr_dczid[] = {
        ARM64_FTR_END,
 };
 
+static const struct arm64_ftr_bits ftr_id_isar0[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_DIVIDE_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_DEBUG_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_COPROC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_CMPBRANCH_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_BITFIELD_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_BITCOUNT_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR0_SWAP_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
 
 static const struct arm64_ftr_bits ftr_id_isar5[] = {
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR5_RDM_SHIFT, 4, 0),
@@ -328,7 +417,37 @@ static const struct arm64_ftr_bits ftr_id_isar5[] = {
 };
 
 static const struct arm64_ftr_bits ftr_id_mmfr4[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_EVT_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_CCIDX_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_LSM_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_HPDS_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_CNP_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR4_XNX_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),        /* ac2 */
+       /*
+        * SpecSEI = 1 indicates that the PE might generate an SError on an
+        * external abort on speculative read. It is safe to assume that an
+        * SError might be generated than it will not be. Hence it has been
+        * classified as FTR_HIGHER_SAFE.
+        */
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_HIGHER_SAFE, ID_MMFR4_SPECSEI_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
+
+static const struct arm64_ftr_bits ftr_id_isar4[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_SWP_FRAC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_PSR_M_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_SYNCH_PRIM_FRAC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_BARRIER_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_SMC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_WRITEBACK_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_WITHSHIFTS_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_ISAR4_UNPRIV_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
+
+static const struct arm64_ftr_bits ftr_id_mmfr5[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_MMFR5_ETS_SHIFT, 4, 0),
        ARM64_FTR_END,
 };
 
@@ -344,6 +463,8 @@ static const struct arm64_ftr_bits ftr_id_isar6[] = {
 };
 
 static const struct arm64_ftr_bits ftr_id_pfr0[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR0_DIT_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR0_CSV2_SHIFT, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),               /* State3 */
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),                /* State2 */
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),                /* State1 */
@@ -351,8 +472,26 @@ static const struct arm64_ftr_bits ftr_id_pfr0[] = {
        ARM64_FTR_END,
 };
 
+static const struct arm64_ftr_bits ftr_id_pfr1[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_GIC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_VIRT_FRAC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_SEC_FRAC_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_GENTIMER_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_VIRTUALIZATION_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_MPROGMOD_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_SECURITY_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR1_PROGMOD_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
+
+static const struct arm64_ftr_bits ftr_id_pfr2[] = {
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_PFR2_SSBS_SHIFT, 4, 0),
+       ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_PFR2_CSV3_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
+
 static const struct arm64_ftr_bits ftr_id_dfr0[] = {
-       ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
+       /* [31:28] TraceFilt */
        S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0xf),   /* PerfMon */
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
@@ -363,6 +502,11 @@ static const struct arm64_ftr_bits ftr_id_dfr0[] = {
        ARM64_FTR_END,
 };
 
+static const struct arm64_ftr_bits ftr_id_dfr1[] = {
+       S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_DFR1_MTPMU_SHIFT, 4, 0),
+       ARM64_FTR_END,
+};
+
 static const struct arm64_ftr_bits ftr_zcr[] = {
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE,
                ZCR_ELx_LEN_SHIFT, ZCR_ELx_LEN_SIZE, 0),        /* LEN */
@@ -373,7 +517,7 @@ static const struct arm64_ftr_bits ftr_zcr[] = {
  * Common ftr bits for a 32bit register with all hidden, strict
  * attributes, with 4bit feature fields and a default safe value of
  * 0. Covers the following 32bit registers:
- * id_isar[0-4], id_mmfr[1-3], id_pfr1, mvfr[0-1]
+ * id_isar[1-4], id_mmfr[1-3], id_pfr1, mvfr[0-1]
  */
 static const struct arm64_ftr_bits ftr_generic_32bits[] = {
        ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
@@ -411,7 +555,7 @@ static const struct __ftr_reg_entry {
 
        /* Op1 = 0, CRn = 0, CRm = 1 */
        ARM64_FTR_REG(SYS_ID_PFR0_EL1, ftr_id_pfr0),
-       ARM64_FTR_REG(SYS_ID_PFR1_EL1, ftr_generic_32bits),
+       ARM64_FTR_REG(SYS_ID_PFR1_EL1, ftr_id_pfr1),
        ARM64_FTR_REG(SYS_ID_DFR0_EL1, ftr_id_dfr0),
        ARM64_FTR_REG(SYS_ID_MMFR0_EL1, ftr_id_mmfr0),
        ARM64_FTR_REG(SYS_ID_MMFR1_EL1, ftr_generic_32bits),
@@ -419,11 +563,11 @@ static const struct __ftr_reg_entry {
        ARM64_FTR_REG(SYS_ID_MMFR3_EL1, ftr_generic_32bits),
 
        /* Op1 = 0, CRn = 0, CRm = 2 */
-       ARM64_FTR_REG(SYS_ID_ISAR0_EL1, ftr_generic_32bits),
+       ARM64_FTR_REG(SYS_ID_ISAR0_EL1, ftr_id_isar0),
        ARM64_FTR_REG(SYS_ID_ISAR1_EL1, ftr_generic_32bits),
        ARM64_FTR_REG(SYS_ID_ISAR2_EL1, ftr_generic_32bits),
        ARM64_FTR_REG(SYS_ID_ISAR3_EL1, ftr_generic_32bits),
-       ARM64_FTR_REG(SYS_ID_ISAR4_EL1, ftr_generic_32bits),
+       ARM64_FTR_REG(SYS_ID_ISAR4_EL1, ftr_id_isar4),
        ARM64_FTR_REG(SYS_ID_ISAR5_EL1, ftr_id_isar5),
        ARM64_FTR_REG(SYS_ID_MMFR4_EL1, ftr_id_mmfr4),
        ARM64_FTR_REG(SYS_ID_ISAR6_EL1, ftr_id_isar6),
@@ -432,6 +576,9 @@ static const struct __ftr_reg_entry {
        ARM64_FTR_REG(SYS_MVFR0_EL1, ftr_generic_32bits),
        ARM64_FTR_REG(SYS_MVFR1_EL1, ftr_generic_32bits),
        ARM64_FTR_REG(SYS_MVFR2_EL1, ftr_mvfr2),
+       ARM64_FTR_REG(SYS_ID_PFR2_EL1, ftr_id_pfr2),
+       ARM64_FTR_REG(SYS_ID_DFR1_EL1, ftr_id_dfr1),
+       ARM64_FTR_REG(SYS_ID_MMFR5_EL1, ftr_id_mmfr5),
 
        /* Op1 = 0, CRn = 0, CRm = 4 */
        ARM64_FTR_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0),
@@ -468,16 +615,16 @@ static int search_cmp_ftr_reg(const void *id, const void *regp)
 }
 
 /*
- * get_arm64_ftr_reg - Lookup a feature register entry using its
- * sys_reg() encoding. With the array arm64_ftr_regs sorted in the
- * ascending order of sys_id , we use binary search to find a matching
+ * get_arm64_ftr_reg_nowarn - Looks up a feature register entry using
+ * its sys_reg() encoding. With the array arm64_ftr_regs sorted in the
+ * ascending order of sys_id, we use binary search to find a matching
  * entry.
  *
  * returns - Upon success,  matching ftr_reg entry for id.
  *         - NULL on failure. It is upto the caller to decide
  *          the impact of a failure.
  */
-static struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id)
+static struct arm64_ftr_reg *get_arm64_ftr_reg_nowarn(u32 sys_id)
 {
        const struct __ftr_reg_entry *ret;
 
@@ -491,6 +638,27 @@ static struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id)
        return NULL;
 }
 
+/*
+ * get_arm64_ftr_reg - Looks up a feature register entry using
+ * its sys_reg() encoding. This calls get_arm64_ftr_reg_nowarn().
+ *
+ * returns - Upon success,  matching ftr_reg entry for id.
+ *         - NULL on failure but with an WARN_ON().
+ */
+static struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id)
+{
+       struct arm64_ftr_reg *reg;
+
+       reg = get_arm64_ftr_reg_nowarn(sys_id);
+
+       /*
+        * Requesting a non-existent register search is an error. Warn
+        * and let the caller handle it.
+        */
+       WARN_ON(!reg);
+       return reg;
+}
+
 static u64 arm64_ftr_set_value(const struct arm64_ftr_bits *ftrp, s64 reg,
                               s64 ftr_val)
 {
@@ -552,7 +720,8 @@ static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
        const struct arm64_ftr_bits *ftrp;
        struct arm64_ftr_reg *reg = get_arm64_ftr_reg(sys_reg);
 
-       BUG_ON(!reg);
+       if (!reg)
+               return;
 
        for (ftrp = reg->ftr_bits; ftrp->width; ftrp++) {
                u64 ftr_mask = arm64_ftr_mask(ftrp);
@@ -625,6 +794,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
 
        if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
                init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
+               init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1);
                init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
                init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
                init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
@@ -636,8 +806,11 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
                init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
                init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
                init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
+               init_cpu_ftr_reg(SYS_ID_MMFR4_EL1, info->reg_id_mmfr4);
+               init_cpu_ftr_reg(SYS_ID_MMFR5_EL1, info->reg_id_mmfr5);
                init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
                init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
+               init_cpu_ftr_reg(SYS_ID_PFR2_EL1, info->reg_id_pfr2);
                init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
                init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
                init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
@@ -682,7 +855,9 @@ static int check_update_ftr_reg(u32 sys_id, int cpu, u64 val, u64 boot)
 {
        struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_id);
 
-       BUG_ON(!regp);
+       if (!regp)
+               return 0;
+
        update_cpu_ftr_reg(regp, val);
        if ((boot & regp->strict_mask) == (val & regp->strict_mask))
                return 0;
@@ -691,6 +866,104 @@ static int check_update_ftr_reg(u32 sys_id, int cpu, u64 val, u64 boot)
        return 1;
 }
 
+static void relax_cpu_ftr_reg(u32 sys_id, int field)
+{
+       const struct arm64_ftr_bits *ftrp;
+       struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_id);
+
+       if (!regp)
+               return;
+
+       for (ftrp = regp->ftr_bits; ftrp->width; ftrp++) {
+               if (ftrp->shift == field) {
+                       regp->strict_mask &= ~arm64_ftr_mask(ftrp);
+                       break;
+               }
+       }
+
+       /* Bogus field? */
+       WARN_ON(!ftrp->width);
+}
+
+static int update_32bit_cpu_features(int cpu, struct cpuinfo_arm64 *info,
+                                    struct cpuinfo_arm64 *boot)
+{
+       int taint = 0;
+       u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
+
+       /*
+        * If we don't have AArch32 at all then skip the checks entirely
+        * as the register values may be UNKNOWN and we're not going to be
+        * using them for anything.
+        */
+       if (!id_aa64pfr0_32bit_el0(pfr0))
+               return taint;
+
+       /*
+        * If we don't have AArch32 at EL1, then relax the strictness of
+        * EL1-dependent register fields to avoid spurious sanity check fails.
+        */
+       if (!id_aa64pfr0_32bit_el1(pfr0)) {
+               relax_cpu_ftr_reg(SYS_ID_ISAR4_EL1, ID_ISAR4_SMC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_VIRT_FRAC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_SEC_FRAC_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_VIRTUALIZATION_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_SECURITY_SHIFT);
+               relax_cpu_ftr_reg(SYS_ID_PFR1_EL1, ID_PFR1_PROGMOD_SHIFT);
+       }
+
+       taint |= check_update_ftr_reg(SYS_ID_DFR0_EL1, cpu,
+                                     info->reg_id_dfr0, boot->reg_id_dfr0);
+       taint |= check_update_ftr_reg(SYS_ID_DFR1_EL1, cpu,
+                                     info->reg_id_dfr1, boot->reg_id_dfr1);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR0_EL1, cpu,
+                                     info->reg_id_isar0, boot->reg_id_isar0);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR1_EL1, cpu,
+                                     info->reg_id_isar1, boot->reg_id_isar1);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR2_EL1, cpu,
+                                     info->reg_id_isar2, boot->reg_id_isar2);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR3_EL1, cpu,
+                                     info->reg_id_isar3, boot->reg_id_isar3);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR4_EL1, cpu,
+                                     info->reg_id_isar4, boot->reg_id_isar4);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR5_EL1, cpu,
+                                     info->reg_id_isar5, boot->reg_id_isar5);
+       taint |= check_update_ftr_reg(SYS_ID_ISAR6_EL1, cpu,
+                                     info->reg_id_isar6, boot->reg_id_isar6);
+
+       /*
+        * Regardless of the value of the AuxReg field, the AIFSR, ADFSR, and
+        * ACTLR formats could differ across CPUs and therefore would have to
+        * be trapped for virtualization anyway.
+        */
+       taint |= check_update_ftr_reg(SYS_ID_MMFR0_EL1, cpu,
+                                     info->reg_id_mmfr0, boot->reg_id_mmfr0);
+       taint |= check_update_ftr_reg(SYS_ID_MMFR1_EL1, cpu,
+                                     info->reg_id_mmfr1, boot->reg_id_mmfr1);
+       taint |= check_update_ftr_reg(SYS_ID_MMFR2_EL1, cpu,
+                                     info->reg_id_mmfr2, boot->reg_id_mmfr2);
+       taint |= check_update_ftr_reg(SYS_ID_MMFR3_EL1, cpu,
+                                     info->reg_id_mmfr3, boot->reg_id_mmfr3);
+       taint |= check_update_ftr_reg(SYS_ID_MMFR4_EL1, cpu,
+                                     info->reg_id_mmfr4, boot->reg_id_mmfr4);
+       taint |= check_update_ftr_reg(SYS_ID_MMFR5_EL1, cpu,
+                                     info->reg_id_mmfr5, boot->reg_id_mmfr5);
+       taint |= check_update_ftr_reg(SYS_ID_PFR0_EL1, cpu,
+                                     info->reg_id_pfr0, boot->reg_id_pfr0);
+       taint |= check_update_ftr_reg(SYS_ID_PFR1_EL1, cpu,
+                                     info->reg_id_pfr1, boot->reg_id_pfr1);
+       taint |= check_update_ftr_reg(SYS_ID_PFR2_EL1, cpu,
+                                     info->reg_id_pfr2, boot->reg_id_pfr2);
+       taint |= check_update_ftr_reg(SYS_MVFR0_EL1, cpu,
+                                     info->reg_mvfr0, boot->reg_mvfr0);
+       taint |= check_update_ftr_reg(SYS_MVFR1_EL1, cpu,
+                                     info->reg_mvfr1, boot->reg_mvfr1);
+       taint |= check_update_ftr_reg(SYS_MVFR2_EL1, cpu,
+                                     info->reg_mvfr2, boot->reg_mvfr2);
+
+       return taint;
+}
+
 /*
  * Update system wide CPU feature registers with the values from a
  * non-boot CPU. Also performs SANITY checks to make sure that there
@@ -753,9 +1026,6 @@ void update_cpu_features(int cpu,
        taint |= check_update_ftr_reg(SYS_ID_AA64MMFR2_EL1, cpu,
                                      info->reg_id_aa64mmfr2, boot->reg_id_aa64mmfr2);
 
-       /*
-        * EL3 is not our concern.
-        */
        taint |= check_update_ftr_reg(SYS_ID_AA64PFR0_EL1, cpu,
                                      info->reg_id_aa64pfr0, boot->reg_id_aa64pfr0);
        taint |= check_update_ftr_reg(SYS_ID_AA64PFR1_EL1, cpu,
@@ -764,55 +1034,6 @@ void update_cpu_features(int cpu,
        taint |= check_update_ftr_reg(SYS_ID_AA64ZFR0_EL1, cpu,
                                      info->reg_id_aa64zfr0, boot->reg_id_aa64zfr0);
 
-       /*
-        * If we have AArch32, we care about 32-bit features for compat.
-        * If the system doesn't support AArch32, don't update them.
-        */
-       if (id_aa64pfr0_32bit_el0(read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1)) &&
-               id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
-
-               taint |= check_update_ftr_reg(SYS_ID_DFR0_EL1, cpu,
-                                       info->reg_id_dfr0, boot->reg_id_dfr0);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR0_EL1, cpu,
-                                       info->reg_id_isar0, boot->reg_id_isar0);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR1_EL1, cpu,
-                                       info->reg_id_isar1, boot->reg_id_isar1);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR2_EL1, cpu,
-                                       info->reg_id_isar2, boot->reg_id_isar2);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR3_EL1, cpu,
-                                       info->reg_id_isar3, boot->reg_id_isar3);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR4_EL1, cpu,
-                                       info->reg_id_isar4, boot->reg_id_isar4);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR5_EL1, cpu,
-                                       info->reg_id_isar5, boot->reg_id_isar5);
-               taint |= check_update_ftr_reg(SYS_ID_ISAR6_EL1, cpu,
-                                       info->reg_id_isar6, boot->reg_id_isar6);
-
-               /*
-                * Regardless of the value of the AuxReg field, the AIFSR, ADFSR, and
-                * ACTLR formats could differ across CPUs and therefore would have to
-                * be trapped for virtualization anyway.
-                */
-               taint |= check_update_ftr_reg(SYS_ID_MMFR0_EL1, cpu,
-                                       info->reg_id_mmfr0, boot->reg_id_mmfr0);
-               taint |= check_update_ftr_reg(SYS_ID_MMFR1_EL1, cpu,
-                                       info->reg_id_mmfr1, boot->reg_id_mmfr1);
-               taint |= check_update_ftr_reg(SYS_ID_MMFR2_EL1, cpu,
-                                       info->reg_id_mmfr2, boot->reg_id_mmfr2);
-               taint |= check_update_ftr_reg(SYS_ID_MMFR3_EL1, cpu,
-                                       info->reg_id_mmfr3, boot->reg_id_mmfr3);
-               taint |= check_update_ftr_reg(SYS_ID_PFR0_EL1, cpu,
-                                       info->reg_id_pfr0, boot->reg_id_pfr0);
-               taint |= check_update_ftr_reg(SYS_ID_PFR1_EL1, cpu,
-                                       info->reg_id_pfr1, boot->reg_id_pfr1);
-               taint |= check_update_ftr_reg(SYS_MVFR0_EL1, cpu,
-                                       info->reg_mvfr0, boot->reg_mvfr0);
-               taint |= check_update_ftr_reg(SYS_MVFR1_EL1, cpu,
-                                       info->reg_mvfr1, boot->reg_mvfr1);
-               taint |= check_update_ftr_reg(SYS_MVFR2_EL1, cpu,
-                                       info->reg_mvfr2, boot->reg_mvfr2);
-       }
-
        if (id_aa64pfr0_sve(info->reg_id_aa64pfr0)) {
                taint |= check_update_ftr_reg(SYS_ZCR_EL1, cpu,
                                        info->reg_zcr, boot->reg_zcr);
@@ -823,6 +1044,12 @@ void update_cpu_features(int cpu,
                        sve_update_vq_map();
        }
 
+       /*
+        * This relies on a sanitised view of the AArch64 ID registers
+        * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last.
+        */
+       taint |= update_32bit_cpu_features(cpu, info, boot);
+
        /*
         * Mismatched CPU features are a recipe for disaster. Don't even
         * pretend to support them.
@@ -837,8 +1064,8 @@ u64 read_sanitised_ftr_reg(u32 id)
 {
        struct arm64_ftr_reg *regp = get_arm64_ftr_reg(id);
 
-       /* We shouldn't get a request for an unsupported register */
-       BUG_ON(!regp);
+       if (!regp)
+               return 0;
        return regp->sys_val;
 }
 
@@ -854,11 +1081,15 @@ static u64 __read_sysreg_by_encoding(u32 sys_id)
        switch (sys_id) {
        read_sysreg_case(SYS_ID_PFR0_EL1);
        read_sysreg_case(SYS_ID_PFR1_EL1);
+       read_sysreg_case(SYS_ID_PFR2_EL1);
        read_sysreg_case(SYS_ID_DFR0_EL1);
+       read_sysreg_case(SYS_ID_DFR1_EL1);
        read_sysreg_case(SYS_ID_MMFR0_EL1);
        read_sysreg_case(SYS_ID_MMFR1_EL1);
        read_sysreg_case(SYS_ID_MMFR2_EL1);
        read_sysreg_case(SYS_ID_MMFR3_EL1);
+       read_sysreg_case(SYS_ID_MMFR4_EL1);
+       read_sysreg_case(SYS_ID_MMFR5_EL1);
        read_sysreg_case(SYS_ID_ISAR0_EL1);
        read_sysreg_case(SYS_ID_ISAR1_EL1);
        read_sysreg_case(SYS_ID_ISAR2_EL1);
@@ -1409,6 +1640,21 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
 }
 #endif
 
+#ifdef CONFIG_ARM64_BTI
+static void bti_enable(const struct arm64_cpu_capabilities *__unused)
+{
+       /*
+        * Use of X16/X17 for tail-calls and trampolines that jump to
+        * function entry points using BR is a requirement for
+        * marking binaries with GNU_PROPERTY_AARCH64_FEATURE_1_BTI.
+        * So, be strict and forbid other BRs using other registers to
+        * jump onto a PACIxSP instruction:
+        */
+       sysreg_clear_set(sctlr_el1, 0, SCTLR_EL1_BT0 | SCTLR_EL1_BT1);
+       isb();
+}
+#endif /* CONFIG_ARM64_BTI */
+
 /* Internal helper functions to match cpu capability type */
 static bool
 cpucap_late_cpu_optional(const struct arm64_cpu_capabilities *cap)
@@ -1511,6 +1757,18 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .field_pos = ID_AA64PFR0_EL0_SHIFT,
                .min_field_value = ID_AA64PFR0_EL0_32BIT_64BIT,
        },
+#ifdef CONFIG_KVM
+       {
+               .desc = "32-bit EL1 Support",
+               .capability = ARM64_HAS_32BIT_EL1,
+               .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+               .matches = has_cpuid_feature,
+               .sys_reg = SYS_ID_AA64PFR0_EL1,
+               .sign = FTR_UNSIGNED,
+               .field_pos = ID_AA64PFR0_EL1_SHIFT,
+               .min_field_value = ID_AA64PFR0_EL1_32BIT_64BIT,
+       },
+#endif
        {
                .desc = "Kernel page table isolation (KPTI)",
                .capability = ARM64_UNMAP_KERNEL_AT_EL0,
@@ -1778,6 +2036,23 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
                .sign = FTR_UNSIGNED,
                .min_field_value = 1,
        },
+#endif
+#ifdef CONFIG_ARM64_BTI
+       {
+               .desc = "Branch Target Identification",
+               .capability = ARM64_BTI,
+#ifdef CONFIG_ARM64_BTI_KERNEL
+               .type = ARM64_CPUCAP_STRICT_BOOT_CPU_FEATURE,
+#else
+               .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+#endif
+               .matches = has_cpuid_feature,
+               .cpu_enable = bti_enable,
+               .sys_reg = SYS_ID_AA64PFR1_EL1,
+               .field_pos = ID_AA64PFR1_BT_SHIFT,
+               .min_field_value = ID_AA64PFR1_BT_BTI,
+               .sign = FTR_UNSIGNED,
+       },
 #endif
        {},
 };
@@ -1888,6 +2163,9 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = {
        HWCAP_CAP(SYS_ID_AA64ZFR0_EL1, ID_AA64ZFR0_F64MM_SHIFT, FTR_UNSIGNED, ID_AA64ZFR0_F64MM, CAP_HWCAP, KERNEL_HWCAP_SVEF64MM),
 #endif
        HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_SSBS_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_SSBS_PSTATE_INSNS, CAP_HWCAP, KERNEL_HWCAP_SSBS),
+#ifdef CONFIG_ARM64_BTI
+       HWCAP_CAP(SYS_ID_AA64PFR1_EL1, ID_AA64PFR1_BT_SHIFT, FTR_UNSIGNED, ID_AA64PFR1_BT_BTI, CAP_HWCAP, KERNEL_HWCAP_BTI),
+#endif
 #ifdef CONFIG_ARM64_PTR_AUTH
        HWCAP_MULTI_CAP(ptr_auth_hwcap_addr_matches, CAP_HWCAP, KERNEL_HWCAP_PACA),
        HWCAP_MULTI_CAP(ptr_auth_hwcap_gen_matches, CAP_HWCAP, KERNEL_HWCAP_PACG),
@@ -2181,6 +2459,36 @@ static void verify_sve_features(void)
        /* Add checks on other ZCR bits here if necessary */
 }
 
+static void verify_hyp_capabilities(void)
+{
+       u64 safe_mmfr1, mmfr0, mmfr1;
+       int parange, ipa_max;
+       unsigned int safe_vmid_bits, vmid_bits;
+
+       if (!IS_ENABLED(CONFIG_KVM) || !IS_ENABLED(CONFIG_KVM_ARM_HOST))
+               return;
+
+       safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
+       mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
+       mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
+
+       /* Verify VMID bits */
+       safe_vmid_bits = get_vmid_bits(safe_mmfr1);
+       vmid_bits = get_vmid_bits(mmfr1);
+       if (vmid_bits < safe_vmid_bits) {
+               pr_crit("CPU%d: VMID width mismatch\n", smp_processor_id());
+               cpu_die_early();
+       }
+
+       /* Verify IPA range */
+       parange = cpuid_feature_extract_unsigned_field(mmfr0,
+                               ID_AA64MMFR0_PARANGE_SHIFT);
+       ipa_max = id_aa64mmfr0_parange_to_phys_shift(parange);
+       if (ipa_max < get_kvm_ipa_limit()) {
+               pr_crit("CPU%d: IPA range mismatch\n", smp_processor_id());
+               cpu_die_early();
+       }
+}
 
 /*
  * Run through the enabled system capabilities and enable() it on this CPU.
@@ -2206,6 +2514,9 @@ static void verify_local_cpu_capabilities(void)
 
        if (system_supports_sve())
                verify_sve_features();
+
+       if (is_hyp_mode_available())
+               verify_hyp_capabilities();
 }
 
 void check_local_cpu_capabilities(void)
@@ -2394,7 +2705,7 @@ static int emulate_sys_reg(u32 id, u64 *valp)
        if (sys_reg_CRm(id) == 0)
                return emulate_id_reg(id, valp);
 
-       regp = get_arm64_ftr_reg(id);
+       regp = get_arm64_ftr_reg_nowarn(id);
        if (regp)
                *valp = arm64_ftr_reg_user_value(regp);
        else
index 86136075ae41042ec32bb0b755ef38a7ce06d051..86637466daa8566646dc6f803a7dee218d4d5be7 100644 (file)
@@ -92,6 +92,7 @@ static const char *const hwcap_str[] = {
        "bf16",
        "dgh",
        "rng",
+       "bti",
        NULL
 };
 
@@ -311,6 +312,8 @@ static int __init cpuinfo_regs_init(void)
        }
        return 0;
 }
+device_initcall(cpuinfo_regs_init);
+
 static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
 {
        unsigned int cpu = smp_processor_id();
@@ -362,6 +365,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
        /* Update the 32bit ID registers only if AArch32 is implemented */
        if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
                info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
+               info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1);
                info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
                info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
                info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
@@ -373,8 +377,11 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
                info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
                info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
                info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
+               info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1);
+               info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1);
                info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
                info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
+               info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1);
 
                info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
                info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
@@ -403,5 +410,3 @@ void __init cpuinfo_store_boot_cpu(void)
        boot_cpu_data = *info;
        init_cpu_features(&boot_cpu_data);
 }
-
-device_initcall(cpuinfo_regs_init);
index ca4c3e12d8c5994abfb4f4ba99bb58f1af966874..1f646b07e3e982408804dffd40591c09fdd91c05 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/crash_core.h>
+#include <asm/cpufeature.h>
 #include <asm/memory.h>
 
 void arch_crash_save_vmcoreinfo(void)
@@ -16,4 +17,7 @@ void arch_crash_save_vmcoreinfo(void)
        vmcoreinfo_append_str("NUMBER(PHYS_OFFSET)=0x%llx\n",
                                                PHYS_OFFSET);
        vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
+       vmcoreinfo_append_str("NUMBER(KERNELPACMASK)=0x%llx\n",
+                                               system_supports_address_auth() ?
+                                               ptrauth_kernel_pac_mask() : 0);
 }
index 48222a4760c2e65229fc1f1cd39f6ecc3e1451a3..15e80c876d464ab28ebb6c5ad7e96512151b7267 100644 (file)
@@ -376,15 +376,13 @@ int aarch32_break_handler(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(aarch32_break_handler);
 
-static int __init debug_traps_init(void)
+void __init debug_traps_init(void)
 {
        hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP,
                              TRAP_TRACE, "single-step handler");
        hook_debug_fault_code(DBG_ESR_EVT_BRK, brk_handler, SIGTRAP,
                              TRAP_BRKPT, "ptrace BRK handler");
-       return 0;
 }
-arch_initcall(debug_traps_init);
 
 /* Re-enable single step for syscall restarting. */
 void user_rewind_single_step(struct task_struct *task)
index 1a03618df0df7fe010cbbfc389eef9b7c902c35f..0073b24b5d25e8f6d77f938c9299262da0d32d25 100644 (file)
 
 SYM_CODE_START(efi_enter_kernel)
        /*
-        * efi_entry() will have copied the kernel image if necessary and we
+        * efi_pe_entry() will have copied the kernel image if necessary and we
         * end up here with device tree address in x1 and the kernel entry
         * point stored in x0. Save those values in registers which are
         * callee preserved.
         */
-       ldr     w2, =stext_offset
+       ldr     w2, =primary_entry_offset
        add     x19, x0, x2             // relocated Image entrypoint
        mov     x20, x1                 // DTB address
 
index 914999ccaf8afad83c69e24782563970782d974b..df67c0f2a077efef312cb6ae53ff542b91f2e83f 100644 (file)
@@ -27,12 +27,12 @@ optional_header:
        .long   __initdata_begin - efi_header_end       // SizeOfCode
        .long   __pecoff_data_size                      // SizeOfInitializedData
        .long   0                                       // SizeOfUninitializedData
-       .long   __efistub_efi_entry - _head             // AddressOfEntryPoint
+       .long   __efistub_efi_pe_entry - _head          // AddressOfEntryPoint
        .long   efi_header_end - _head                  // BaseOfCode
 
 extra_header_fields:
        .quad   0                                       // ImageBase
-       .long   SZ_4K                                   // SectionAlignment
+       .long   SEGMENT_ALIGN                           // SectionAlignment
        .long   PECOFF_FILE_ALIGNMENT                   // FileAlignment
        .short  0                                       // MajorOperatingSystemVersion
        .short  0                                       // MinorOperatingSystemVersion
index 3fc71106cb2b45eb0dd7091c7da824856bae4ea9..75691a2641c1c0f8ec05604ae6b47345fcb93e75 100644 (file)
@@ -5,7 +5,7 @@
 
 #include <linux/linkage.h>
 
-ENTRY(__efi_rt_asm_wrapper)
+SYM_FUNC_START(__efi_rt_asm_wrapper)
        stp     x29, x30, [sp, #-32]!
        mov     x29, sp
 
@@ -34,5 +34,14 @@ ENTRY(__efi_rt_asm_wrapper)
        ldp     x29, x30, [sp], #32
        b.ne    0f
        ret
-0:     b       efi_handle_corrupted_x18        // tail call
-ENDPROC(__efi_rt_asm_wrapper)
+0:
+       /*
+        * With CONFIG_SHADOW_CALL_STACK, the kernel uses x18 to store a
+        * shadow stack pointer, which we need to restore before returning to
+        * potentially instrumented code. This is safe because the wrapper is
+        * called with preemption disabled and a separate shadow stack is used
+        * for interrupts.
+        */
+       mov     x18, x2
+       b       efi_handle_corrupted_x18        // tail call
+SYM_FUNC_END(__efi_rt_asm_wrapper)
index c839b5bf1904b128b0fe7f91d569397b8566d80a..3dbdf9752b118fd45784a48a7bdd85c6b25e2f1b 100644 (file)
@@ -94,7 +94,7 @@ asmlinkage void notrace el1_sync_handler(struct pt_regs *regs)
                break;
        default:
                el1_inv(regs, esr);
-       };
+       }
 }
 NOKPROBE_SYMBOL(el1_sync_handler);
 
@@ -188,6 +188,14 @@ static void notrace el0_undef(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(el0_undef);
 
+static void notrace el0_bti(struct pt_regs *regs)
+{
+       user_exit_irqoff();
+       local_daif_restore(DAIF_PROCCTX);
+       do_bti(regs);
+}
+NOKPROBE_SYMBOL(el0_bti);
+
 static void notrace el0_inv(struct pt_regs *regs, unsigned long esr)
 {
        user_exit_irqoff();
@@ -255,6 +263,9 @@ asmlinkage void notrace el0_sync_handler(struct pt_regs *regs)
        case ESR_ELx_EC_UNKNOWN:
                el0_undef(regs);
                break;
+       case ESR_ELx_EC_BTI:
+               el0_bti(regs);
+               break;
        case ESR_ELx_EC_BREAKPT_LOW:
        case ESR_ELx_EC_SOFTSTP_LOW:
        case ESR_ELx_EC_WATCHPT_LOW:
index 0f24eae8f3cceccee6bf4f20b19eb31d4e67ce83..f880dd63ddc38025371ebd889be0ffff572869c7 100644 (file)
  *
  * x0 - pointer to struct fpsimd_state
  */
-ENTRY(fpsimd_save_state)
+SYM_FUNC_START(fpsimd_save_state)
        fpsimd_save x0, 8
        ret
-ENDPROC(fpsimd_save_state)
+SYM_FUNC_END(fpsimd_save_state)
 
 /*
  * Load the FP registers.
  *
  * x0 - pointer to struct fpsimd_state
  */
-ENTRY(fpsimd_load_state)
+SYM_FUNC_START(fpsimd_load_state)
        fpsimd_restore x0, 8
        ret
-ENDPROC(fpsimd_load_state)
+SYM_FUNC_END(fpsimd_load_state)
 
 #ifdef CONFIG_ARM64_SVE
-ENTRY(sve_save_state)
+SYM_FUNC_START(sve_save_state)
        sve_save 0, x1, 2
        ret
-ENDPROC(sve_save_state)
+SYM_FUNC_END(sve_save_state)
 
-ENTRY(sve_load_state)
+SYM_FUNC_START(sve_load_state)
        sve_load 0, x1, x2, 3, x4
        ret
-ENDPROC(sve_load_state)
+SYM_FUNC_END(sve_load_state)
 
-ENTRY(sve_get_vl)
+SYM_FUNC_START(sve_get_vl)
        _sve_rdvl       0, 1
        ret
-ENDPROC(sve_get_vl)
+SYM_FUNC_END(sve_get_vl)
 #endif /* CONFIG_ARM64_SVE */
index 833d48c9acb5ce9225cae9eb6ac200d9a49761d4..a338f40e64d393b2f78d5b01b4bda24930e0db87 100644 (file)
@@ -23,8 +23,9 @@
  *
  * ... where <entry> is either ftrace_caller or ftrace_regs_caller.
  *
- * Each instrumented function follows the AAPCS, so here x0-x8 and x19-x30 are
- * live, and x9-x18 are safe to clobber.
+ * Each instrumented function follows the AAPCS, so here x0-x8 and x18-x30 are
+ * live (x18 holds the Shadow Call Stack pointer), and x9-x17 are safe to
+ * clobber.
  *
  * We save the callsite's context into a pt_regs before invoking any ftrace
  * callbacks. So that we can get a sensible backtrace, we create a stack record
index ddcde093c433b83e0c86d28d7a5d5f81367d6c86..5304d193c79dd3a67bca8d72ec9afbf1d530df2e 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/mmu.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
+#include <asm/scs.h>
 #include <asm/thread_info.h>
 #include <asm/asm-uaccess.h>
 #include <asm/unistd.h>
@@ -178,7 +179,9 @@ alternative_cb_end
 
        apply_ssbd 1, x22, x23
 
-       ptrauth_keys_install_kernel tsk, 1, x20, x22, x23
+       ptrauth_keys_install_kernel tsk, x20, x22, x23
+
+       scs_load tsk, x20
        .else
        add     x21, sp, #S_FRAME_SIZE
        get_current_task tsk
@@ -343,6 +346,8 @@ alternative_else_nop_endif
        msr     cntkctl_el1, x1
 4:
 #endif
+       scs_save tsk, x0
+
        /* No kernel C function calls after this as user keys are set. */
        ptrauth_keys_install_user tsk, x0, x1, x2
 
@@ -388,6 +393,9 @@ alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0
 
        .macro  irq_stack_entry
        mov     x19, sp                 // preserve the original sp
+#ifdef CONFIG_SHADOW_CALL_STACK
+       mov     x24, scs_sp             // preserve the original shadow stack
+#endif
 
        /*
         * Compare sp with the base of the task stack.
@@ -405,15 +413,25 @@ alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0
 
        /* switch to the irq stack */
        mov     sp, x26
+
+#ifdef CONFIG_SHADOW_CALL_STACK
+       /* also switch to the irq shadow stack */
+       adr_this_cpu scs_sp, irq_shadow_call_stack, x26
+#endif
+
 9998:
        .endm
 
        /*
-        * x19 should be preserved between irq_stack_entry and
-        * irq_stack_exit.
+        * The callee-saved regs (x19-x29) should be preserved between
+        * irq_stack_entry and irq_stack_exit, but note that kernel_entry
+        * uses x20-x23 to store data for later use.
         */
        .macro  irq_stack_exit
        mov     sp, x19
+#ifdef CONFIG_SHADOW_CALL_STACK
+       mov     scs_sp, x24
+#endif
        .endm
 
 /* GPRs used by entry code */
@@ -727,21 +745,10 @@ el0_error_naked:
        b       ret_to_user
 SYM_CODE_END(el0_error)
 
-/*
- * Ok, we need to do extra processing, enter the slow path.
- */
-work_pending:
-       mov     x0, sp                          // 'regs'
-       bl      do_notify_resume
-#ifdef CONFIG_TRACE_IRQFLAGS
-       bl      trace_hardirqs_on               // enabled while in userspace
-#endif
-       ldr     x1, [tsk, #TSK_TI_FLAGS]        // re-check for single-step
-       b       finish_ret_to_user
 /*
  * "slow" syscall return path.
  */
-ret_to_user:
+SYM_CODE_START_LOCAL(ret_to_user)
        disable_daif
        gic_prio_kentry_setup tmp=x3
        ldr     x1, [tsk, #TSK_TI_FLAGS]
@@ -753,7 +760,19 @@ finish_ret_to_user:
        bl      stackleak_erase
 #endif
        kernel_exit 0
-ENDPROC(ret_to_user)
+
+/*
+ * Ok, we need to do extra processing, enter the slow path.
+ */
+work_pending:
+       mov     x0, sp                          // 'regs'
+       bl      do_notify_resume
+#ifdef CONFIG_TRACE_IRQFLAGS
+       bl      trace_hardirqs_on               // enabled while in userspace
+#endif
+       ldr     x1, [tsk, #TSK_TI_FLAGS]        // re-check for single-step
+       b       finish_ret_to_user
+SYM_CODE_END(ret_to_user)
 
        .popsection                             // .entry.text
 
@@ -900,7 +919,9 @@ SYM_FUNC_START(cpu_switch_to)
        ldr     lr, [x8]
        mov     sp, x9
        msr     sp_el0, x1
-       ptrauth_keys_install_kernel x1, 1, x8, x9, x10
+       ptrauth_keys_install_kernel x1, x8, x9, x10
+       scs_save x0, x8
+       scs_load x1, x8
        ret
 SYM_FUNC_END(cpu_switch_to)
 NOKPROBE(cpu_switch_to)
@@ -1029,13 +1050,16 @@ SYM_CODE_START(__sdei_asm_handler)
 
        mov     x19, x1
 
+#if defined(CONFIG_VMAP_STACK) || defined(CONFIG_SHADOW_CALL_STACK)
+       ldrb    w4, [x19, #SDEI_EVENT_PRIORITY]
+#endif
+
 #ifdef CONFIG_VMAP_STACK
        /*
         * entry.S may have been using sp as a scratch register, find whether
         * this is a normal or critical event and switch to the appropriate
         * stack for this CPU.
         */
-       ldrb    w4, [x19, #SDEI_EVENT_PRIORITY]
        cbnz    w4, 1f
        ldr_this_cpu dst=x5, sym=sdei_stack_normal_ptr, tmp=x6
        b       2f
@@ -1045,6 +1069,15 @@ SYM_CODE_START(__sdei_asm_handler)
        mov     sp, x5
 #endif
 
+#ifdef CONFIG_SHADOW_CALL_STACK
+       /* Use a separate shadow call stack for normal and critical events */
+       cbnz    w4, 3f
+       adr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_normal, tmp=x6
+       b       4f
+3:     adr_this_cpu dst=scs_sp, sym=sdei_shadow_call_stack_critical, tmp=x6
+4:
+#endif
+
        /*
         * We may have interrupted userspace, or a guest, or exit-from or
         * return-to either of these. We can't trust sp_el0, restore it.
index 57a91032b4c21ca2dc74447c10093f0d4dd0cec7..632702146813a36ecfb3fb6bd7e9e5e122b99014 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/irqchip/arm-gic-v3.h>
 
+#include <asm/asm_pointer_auth.h>
 #include <asm/assembler.h>
 #include <asm/boot.h>
 #include <asm/ptrace.h>
@@ -27,6 +28,7 @@
 #include <asm/pgtable-hwdef.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#include <asm/scs.h>
 #include <asm/smp.h>
 #include <asm/sysreg.h>
 #include <asm/thread_info.h>
@@ -70,9 +72,9 @@ _head:
         * its opcode forms the magic "MZ" signature required by UEFI.
         */
        add     x13, x18, #0x16
-       b       stext
+       b       primary_entry
 #else
-       b       stext                           // branch to kernel start, magic
+       b       primary_entry                   // branch to kernel start, magic
        .long   0                               // reserved
 #endif
        le64sym _kernel_offset_le               // Image load offset from start of RAM, little-endian
@@ -98,14 +100,13 @@ pe_header:
         * primary lowlevel boot path:
         *
         *  Register   Scope                      Purpose
-        *  x21        stext() .. start_kernel()  FDT pointer passed at boot in x0
-        *  x23        stext() .. start_kernel()  physical misalignment/KASLR offset
-        *  x28        __create_page_tables()     callee preserved temp register
-        *  x19/x20    __primary_switch()         callee preserved temp registers
-        *  x24        __primary_switch() .. relocate_kernel()
-        *                                        current RELR displacement
+        *  x21        primary_entry() .. start_kernel()        FDT pointer passed at boot in x0
+        *  x23        primary_entry() .. start_kernel()        physical misalignment/KASLR offset
+        *  x28        __create_page_tables()                   callee preserved temp register
+        *  x19/x20    __primary_switch()                       callee preserved temp registers
+        *  x24        __primary_switch() .. relocate_kernel()  current RELR displacement
         */
-SYM_CODE_START(stext)
+SYM_CODE_START(primary_entry)
        bl      preserve_boot_args
        bl      el2_setup                       // Drop to EL1, w0=cpu_boot_mode
        adrp    x23, __PHYS_OFFSET
@@ -118,10 +119,9 @@ SYM_CODE_START(stext)
         * On return, the CPU will be ready for the MMU to be turned on and
         * the TCR will have been set.
         */
-       mov     x0, #ARM64_CPU_BOOT_PRIMARY
        bl      __cpu_setup                     // initialise processor
        b       __primary_switch
-SYM_CODE_END(stext)
+SYM_CODE_END(primary_entry)
 
 /*
  * Preserve the arguments passed by the bootloader in x0 .. x3
@@ -394,13 +394,19 @@ SYM_FUNC_START_LOCAL(__create_page_tables)
 
        /*
         * Since the page tables have been populated with non-cacheable
-        * accesses (MMU disabled), invalidate the idmap and swapper page
-        * tables again to remove any speculatively loaded cache lines.
+        * accesses (MMU disabled), invalidate those tables again to
+        * remove any speculatively loaded cache lines.
         */
+       dmb     sy
+
        adrp    x0, idmap_pg_dir
+       adrp    x1, idmap_pg_end
+       sub     x1, x1, x0
+       bl      __inval_dcache_area
+
+       adrp    x0, init_pg_dir
        adrp    x1, init_pg_end
        sub     x1, x1, x0
-       dmb     sy
        bl      __inval_dcache_area
 
        ret     x28
@@ -417,6 +423,10 @@ SYM_FUNC_START_LOCAL(__primary_switched)
        adr_l   x5, init_task
        msr     sp_el0, x5                      // Save thread_info
 
+#ifdef CONFIG_ARM64_PTR_AUTH
+       __ptrauth_keys_init_cpu x5, x6, x7, x8
+#endif
+
        adr_l   x8, vectors                     // load VBAR_EL1 with virtual
        msr     vbar_el1, x8                    // vector table address
        isb
@@ -424,6 +434,10 @@ SYM_FUNC_START_LOCAL(__primary_switched)
        stp     xzr, x30, [sp, #-16]!
        mov     x29, sp
 
+#ifdef CONFIG_SHADOW_CALL_STACK
+       adr_l   scs_sp, init_shadow_call_stack  // Set shadow call stack
+#endif
+
        str_l   x21, __fdt_pointer, x5          // Save FDT pointer
 
        ldr_l   x4, kimage_vaddr                // Save the offset between
@@ -717,7 +731,6 @@ SYM_FUNC_START_LOCAL(secondary_startup)
         * Common entry point for secondary CPUs.
         */
        bl      __cpu_secondary_check52bitva
-       mov     x0, #ARM64_CPU_BOOT_SECONDARY
        bl      __cpu_setup                     // initialise processor
        adrp    x1, swapper_pg_dir
        bl      __enable_mmu
@@ -737,8 +750,14 @@ SYM_FUNC_START_LOCAL(__secondary_switched)
        ldr     x2, [x0, #CPU_BOOT_TASK]
        cbz     x2, __secondary_too_slow
        msr     sp_el0, x2
+       scs_load x2, x3
        mov     x29, #0
        mov     x30, #0
+
+#ifdef CONFIG_ARM64_PTR_AUTH
+       ptrauth_keys_init_cpu x2, x3, x4, x5
+#endif
+
        b       secondary_start_kernel
 SYM_FUNC_END(__secondary_switched)
 
index 6532105b3e32683de63f6c9f97d4d0d1e7b461ce..8ccca660034e4859bfb821f3053f49946fb258d4 100644 (file)
@@ -65,7 +65,7 @@
  * x5: physical address of a  zero page that remains zero after resume
  */
 .pushsection    ".hibernate_exit.text", "ax"
-ENTRY(swsusp_arch_suspend_exit)
+SYM_CODE_START(swsusp_arch_suspend_exit)
        /*
         * We execute from ttbr0, change ttbr1 to our copied linear map tables
         * with a break-before-make via the zero page
@@ -110,7 +110,7 @@ ENTRY(swsusp_arch_suspend_exit)
        cbz     x24, 3f         /* Do we need to re-initialise EL2? */
        hvc     #0
 3:     ret
-ENDPROC(swsusp_arch_suspend_exit)
+SYM_CODE_END(swsusp_arch_suspend_exit)
 
 /*
  * Restore the hyp stub.
@@ -119,15 +119,15 @@ ENDPROC(swsusp_arch_suspend_exit)
  *
  * x24: The physical address of __hyp_stub_vectors
  */
-el1_sync:
+SYM_CODE_START_LOCAL(el1_sync)
        msr     vbar_el2, x24
        eret
-ENDPROC(el1_sync)
+SYM_CODE_END(el1_sync)
 
 .macro invalid_vector  label
-\label:
+SYM_CODE_START_LOCAL(\label)
        b \label
-ENDPROC(\label)
+SYM_CODE_END(\label)
 .endm
 
        invalid_vector  el2_sync_invalid
@@ -141,7 +141,7 @@ ENDPROC(\label)
 
 /* el2 vectors - switch el2 here while we restore the memory image. */
        .align 11
-ENTRY(hibernate_el2_vectors)
+SYM_CODE_START(hibernate_el2_vectors)
        ventry  el2_sync_invalid                // Synchronous EL2t
        ventry  el2_irq_invalid                 // IRQ EL2t
        ventry  el2_fiq_invalid                 // FIQ EL2t
@@ -161,6 +161,6 @@ ENTRY(hibernate_el2_vectors)
        ventry  el1_irq_invalid                 // IRQ 32-bit EL1
        ventry  el1_fiq_invalid                 // FIQ 32-bit EL1
        ventry  el1_error_invalid               // Error 32-bit EL1
-END(hibernate_el2_vectors)
+SYM_CODE_END(hibernate_el2_vectors)
 
 .popsection
index e473ead806ed0953f9ebeb3879e9e8af3822b548..160f5881a0b7fb58eb859fc3bef516a88123bb9f 100644 (file)
@@ -21,7 +21,7 @@
 
        .align 11
 
-ENTRY(__hyp_stub_vectors)
+SYM_CODE_START(__hyp_stub_vectors)
        ventry  el2_sync_invalid                // Synchronous EL2t
        ventry  el2_irq_invalid                 // IRQ EL2t
        ventry  el2_fiq_invalid                 // FIQ EL2t
@@ -41,11 +41,11 @@ ENTRY(__hyp_stub_vectors)
        ventry  el1_irq_invalid                 // IRQ 32-bit EL1
        ventry  el1_fiq_invalid                 // FIQ 32-bit EL1
        ventry  el1_error_invalid               // Error 32-bit EL1
-ENDPROC(__hyp_stub_vectors)
+SYM_CODE_END(__hyp_stub_vectors)
 
        .align 11
 
-el1_sync:
+SYM_CODE_START_LOCAL(el1_sync)
        cmp     x0, #HVC_SET_VECTORS
        b.ne    2f
        msr     vbar_el2, x1
@@ -68,12 +68,12 @@ el1_sync:
 
 9:     mov     x0, xzr
        eret
-ENDPROC(el1_sync)
+SYM_CODE_END(el1_sync)
 
 .macro invalid_vector  label
-\label:
+SYM_CODE_START_LOCAL(\label)
        b \label
-ENDPROC(\label)
+SYM_CODE_END(\label)
 .endm
 
        invalid_vector  el2_sync_invalid
@@ -106,15 +106,15 @@ ENDPROC(\label)
  * initialisation entry point.
  */
 
-ENTRY(__hyp_set_vectors)
+SYM_FUNC_START(__hyp_set_vectors)
        mov     x1, x0
        mov     x0, #HVC_SET_VECTORS
        hvc     #0
        ret
-ENDPROC(__hyp_set_vectors)
+SYM_FUNC_END(__hyp_set_vectors)
 
-ENTRY(__hyp_reset_vectors)
+SYM_FUNC_START(__hyp_reset_vectors)
        mov     x0, #HVC_RESET_VECTORS
        hvc     #0
        ret
-ENDPROC(__hyp_reset_vectors)
+SYM_FUNC_END(__hyp_reset_vectors)
index 7f06ad93fc95884df23825a0c318cf97e99e7183..be0a63ffed239089b16935a5df4080c6f02966ba 100644 (file)
@@ -13,7 +13,7 @@
 #ifdef CONFIG_EFI
 
 __efistub_kernel_size          = _edata - _text;
-__efistub_stext_offset         = stext - _text;
+__efistub_primary_entry_offset = primary_entry - _text;
 
 
 /*
index 4a9e773a177f0782933c0c8f516ce31241cf67bc..684d871ae38dc4fc5ba3e95fd63c2998799efacf 100644 (file)
@@ -51,21 +51,33 @@ enum aarch64_insn_encoding_class __kprobes aarch64_get_insn_class(u32 insn)
        return aarch64_insn_encoding_class[(insn >> 25) & 0xf];
 }
 
-/* NOP is an alias of HINT */
-bool __kprobes aarch64_insn_is_nop(u32 insn)
+bool __kprobes aarch64_insn_is_steppable_hint(u32 insn)
 {
        if (!aarch64_insn_is_hint(insn))
                return false;
 
        switch (insn & 0xFE0) {
-       case AARCH64_INSN_HINT_YIELD:
-       case AARCH64_INSN_HINT_WFE:
-       case AARCH64_INSN_HINT_WFI:
-       case AARCH64_INSN_HINT_SEV:
-       case AARCH64_INSN_HINT_SEVL:
-               return false;
-       default:
+       case AARCH64_INSN_HINT_XPACLRI:
+       case AARCH64_INSN_HINT_PACIA_1716:
+       case AARCH64_INSN_HINT_PACIB_1716:
+       case AARCH64_INSN_HINT_AUTIA_1716:
+       case AARCH64_INSN_HINT_AUTIB_1716:
+       case AARCH64_INSN_HINT_PACIAZ:
+       case AARCH64_INSN_HINT_PACIASP:
+       case AARCH64_INSN_HINT_PACIBZ:
+       case AARCH64_INSN_HINT_PACIBSP:
+       case AARCH64_INSN_HINT_AUTIAZ:
+       case AARCH64_INSN_HINT_AUTIASP:
+       case AARCH64_INSN_HINT_AUTIBZ:
+       case AARCH64_INSN_HINT_AUTIBSP:
+       case AARCH64_INSN_HINT_BTI:
+       case AARCH64_INSN_HINT_BTIC:
+       case AARCH64_INSN_HINT_BTIJ:
+       case AARCH64_INSN_HINT_BTIJC:
+       case AARCH64_INSN_HINT_NOP:
                return true;
+       default:
+               return false;
        }
 }
 
@@ -574,7 +586,7 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
                                             offset >> 2);
 }
 
-u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op)
+u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_cr_op op)
 {
        return aarch64_insn_get_hint_value() | op;
 }
@@ -1535,16 +1547,10 @@ static u32 aarch64_encode_immediate(u64 imm,
                                    u32 insn)
 {
        unsigned int immr, imms, n, ones, ror, esz, tmp;
-       u64 mask = ~0UL;
-
-       /* Can't encode full zeroes or full ones */
-       if (!imm || !~imm)
-               return AARCH64_BREAK_FAULT;
+       u64 mask;
 
        switch (variant) {
        case AARCH64_INSN_VARIANT_32BIT:
-               if (upper_32_bits(imm))
-                       return AARCH64_BREAK_FAULT;
                esz = 32;
                break;
        case AARCH64_INSN_VARIANT_64BIT:
@@ -1556,6 +1562,12 @@ static u32 aarch64_encode_immediate(u64 imm,
                return AARCH64_BREAK_FAULT;
        }
 
+       mask = GENMASK(esz - 1, 0);
+
+       /* Can't encode full zeroes, full ones, or value wider than the mask */
+       if (!imm || imm == mask || imm & ~mask)
+               return AARCH64_BREAK_FAULT;
+
        /*
         * Inverse of Replicate(). Try to spot a repeating pattern
         * with a pow2 stride.
index b40c3b0def920d88c997fb3291318dd634451466..522e6f517ec0d6303bd7399bc44eb2e27197f4e1 100644 (file)
@@ -138,12 +138,12 @@ static int setup_dtb(struct kimage *image,
 
        /* add rng-seed */
        if (rng_is_initialized()) {
-               u8 rng_seed[RNG_SEED_SIZE];
-               get_random_bytes(rng_seed, RNG_SEED_SIZE);
-               ret = fdt_setprop(dtb, off, FDT_PROP_RNG_SEED, rng_seed,
-                               RNG_SEED_SIZE);
+               void *rng_seed;
+               ret = fdt_setprop_placeholder(dtb, off, FDT_PROP_RNG_SEED,
+                               RNG_SEED_SIZE, &rng_seed);
                if (ret)
                        goto out;
+               get_random_bytes(rng_seed, RNG_SEED_SIZE);
        } else {
                pr_notice("RNG is not initialised: omitting \"%s\" property\n",
                                FDT_PROP_RNG_SEED);
@@ -284,7 +284,7 @@ int load_other_segments(struct kimage *image,
                image->arch.elf_headers_sz = headers_sz;
 
                pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
-                        image->arch.elf_headers_mem, headers_sz, headers_sz);
+                        image->arch.elf_headers_mem, kbuf.bufsz, kbuf.memsz);
        }
 
        /* load initrd */
@@ -305,7 +305,7 @@ int load_other_segments(struct kimage *image,
                initrd_load_addr = kbuf.mem;
 
                pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
-                               initrd_load_addr, initrd_len, initrd_len);
+                               initrd_load_addr, kbuf.bufsz, kbuf.memsz);
        }
 
        /* load dtb */
@@ -332,7 +332,7 @@ int load_other_segments(struct kimage *image,
        image->arch.dtb_mem = kbuf.mem;
 
        pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
-                       kbuf.mem, dtb_len, dtb_len);
+                       kbuf.mem, kbuf.bufsz, kbuf.memsz);
 
        return 0;
 
index 1ef702b0be2dc7d9a3063184aa409d390a2671df..295d66490584bb8ec31073d77f3a99590f848b38 100644 (file)
@@ -120,7 +120,7 @@ static bool has_pv_steal_clock(void)
        struct arm_smccc_res res;
 
        /* To detect the presence of PV time support we require SMCCC 1.1+ */
-       if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
+       if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
                return false;
 
        arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
index b78fac9e546c5061cad3a9a0038fdb06a84d0a0f..263d5fba4c8a3c833626b63525ead8f65e58a255 100644 (file)
@@ -46,7 +46,7 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)
                 * except for the NOP case.
                 */
                if (aarch64_insn_is_hint(insn))
-                       return aarch64_insn_is_nop(insn);
+                       return aarch64_insn_is_steppable_hint(insn);
 
                return true;
        }
index 45dce03aaeafc12ab196d6b444e9331654b7fad2..890ca72c5a5148a662c52de78f42c81c88abf420 100644 (file)
@@ -61,7 +61,7 @@
        ldp x28, x29, [sp, #S_X28]
        .endm
 
-ENTRY(kretprobe_trampoline)
+SYM_CODE_START(kretprobe_trampoline)
        sub sp, sp, #S_FRAME_SIZE
 
        save_all_base_regs
@@ -79,4 +79,4 @@ ENTRY(kretprobe_trampoline)
        add sp, sp, #S_FRAME_SIZE
        ret
 
-ENDPROC(kretprobe_trampoline)
+SYM_CODE_END(kretprobe_trampoline)
index 56be4cbf771f604a849f958382aec9acdf4e837f..eade7807e819d5637ea157315819ca136153e43d 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/compat.h>
 #include <linux/efi.h>
+#include <linux/elf.h>
 #include <linux/export.h>
 #include <linux/sched.h>
 #include <linux/sched/debug.h>
@@ -18,6 +19,7 @@
 #include <linux/sched/task_stack.h>
 #include <linux/kernel.h>
 #include <linux/lockdep.h>
+#include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
 #include <linux/sysctl.h>
@@ -209,6 +211,15 @@ void machine_restart(char *cmd)
        while (1);
 }
 
+#define bstr(suffix, str) [PSR_BTYPE_ ## suffix >> PSR_BTYPE_SHIFT] = str
+static const char *const btypes[] = {
+       bstr(NONE, "--"),
+       bstr(  JC, "jc"),
+       bstr(   C, "-c"),
+       bstr(  J , "j-")
+};
+#undef bstr
+
 static void print_pstate(struct pt_regs *regs)
 {
        u64 pstate = regs->pstate;
@@ -227,7 +238,10 @@ static void print_pstate(struct pt_regs *regs)
                        pstate & PSR_AA32_I_BIT ? 'I' : 'i',
                        pstate & PSR_AA32_F_BIT ? 'F' : 'f');
        } else {
-               printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO)\n",
+               const char *btype_str = btypes[(pstate & PSR_BTYPE_MASK) >>
+                                              PSR_BTYPE_SHIFT];
+
+               printk("pstate: %08llx (%c%c%c%c %c%c%c%c %cPAN %cUAO BTYPE=%s)\n",
                        pstate,
                        pstate & PSR_N_BIT ? 'N' : 'n',
                        pstate & PSR_Z_BIT ? 'Z' : 'z',
@@ -238,7 +252,8 @@ static void print_pstate(struct pt_regs *regs)
                        pstate & PSR_I_BIT ? 'I' : 'i',
                        pstate & PSR_F_BIT ? 'F' : 'f',
                        pstate & PSR_PAN_BIT ? '+' : '-',
-                       pstate & PSR_UAO_BIT ? '+' : '-');
+                       pstate & PSR_UAO_BIT ? '+' : '-',
+                       btype_str);
        }
 }
 
@@ -655,3 +670,25 @@ asmlinkage void __sched arm64_preempt_schedule_irq(void)
        if (system_capabilities_finalized())
                preempt_schedule_irq();
 }
+
+#ifdef CONFIG_BINFMT_ELF
+int arch_elf_adjust_prot(int prot, const struct arch_elf_state *state,
+                        bool has_interp, bool is_interp)
+{
+       /*
+        * For dynamically linked executables the interpreter is
+        * responsible for setting PROT_BTI on everything except
+        * itself.
+        */
+       if (is_interp != has_interp)
+               return prot;
+
+       if (!(state->flags & ARM64_ELF_BTI))
+               return prot;
+
+       if (prot & PROT_EXEC)
+               prot |= PROT_BTI;
+
+       return prot;
+}
+#endif
index e7b01904f18017cc1cf49ac39a1eb07a29561691..76790a5f2a0dd17e156ce73cc63983b743884a7c 100644 (file)
@@ -1875,7 +1875,7 @@ void syscall_trace_exit(struct pt_regs *regs)
  */
 #define SPSR_EL1_AARCH64_RES0_BITS \
        (GENMASK_ULL(63, 32) | GENMASK_ULL(27, 25) | GENMASK_ULL(23, 22) | \
-        GENMASK_ULL(20, 13) | GENMASK_ULL(11, 10) | GENMASK_ULL(5, 5))
+        GENMASK_ULL(20, 13) | GENMASK_ULL(5, 5))
 #define SPSR_EL1_AARCH32_RES0_BITS \
        (GENMASK_ULL(63, 32) | GENMASK_ULL(22, 22) | GENMASK_ULL(20, 20))
 
index 16a34f188f2672d01298b42cc7385da6c78bc14b..c50f45fa29fa642fb60f996c81deefa466257ee7 100644 (file)
@@ -5,81 +5,81 @@
 
 #include <linux/linkage.h>
 
-ENTRY(absolute_data64)
+SYM_FUNC_START(absolute_data64)
        ldr     x0, 0f
        ret
 0:     .quad   sym64_abs
-ENDPROC(absolute_data64)
+SYM_FUNC_END(absolute_data64)
 
-ENTRY(absolute_data32)
+SYM_FUNC_START(absolute_data32)
        ldr     w0, 0f
        ret
 0:     .long   sym32_abs
-ENDPROC(absolute_data32)
+SYM_FUNC_END(absolute_data32)
 
-ENTRY(absolute_data16)
+SYM_FUNC_START(absolute_data16)
        adr     x0, 0f
        ldrh    w0, [x0]
        ret
 0:     .short  sym16_abs, 0
-ENDPROC(absolute_data16)
+SYM_FUNC_END(absolute_data16)
 
-ENTRY(signed_movw)
+SYM_FUNC_START(signed_movw)
        movz    x0, #:abs_g2_s:sym64_abs
        movk    x0, #:abs_g1_nc:sym64_abs
        movk    x0, #:abs_g0_nc:sym64_abs
        ret
-ENDPROC(signed_movw)
+SYM_FUNC_END(signed_movw)
 
-ENTRY(unsigned_movw)
+SYM_FUNC_START(unsigned_movw)
        movz    x0, #:abs_g3:sym64_abs
        movk    x0, #:abs_g2_nc:sym64_abs
        movk    x0, #:abs_g1_nc:sym64_abs
        movk    x0, #:abs_g0_nc:sym64_abs
        ret
-ENDPROC(unsigned_movw)
+SYM_FUNC_END(unsigned_movw)
 
        .align  12
        .space  0xff8
-ENTRY(relative_adrp)
+SYM_FUNC_START(relative_adrp)
        adrp    x0, sym64_rel
        add     x0, x0, #:lo12:sym64_rel
        ret
-ENDPROC(relative_adrp)
+SYM_FUNC_END(relative_adrp)
 
        .align  12
        .space  0xffc
-ENTRY(relative_adrp_far)
+SYM_FUNC_START(relative_adrp_far)
        adrp    x0, memstart_addr
        add     x0, x0, #:lo12:memstart_addr
        ret
-ENDPROC(relative_adrp_far)
+SYM_FUNC_END(relative_adrp_far)
 
-ENTRY(relative_adr)
+SYM_FUNC_START(relative_adr)
        adr     x0, sym64_rel
        ret
-ENDPROC(relative_adr)
+SYM_FUNC_END(relative_adr)
 
-ENTRY(relative_data64)
+SYM_FUNC_START(relative_data64)
        adr     x1, 0f
        ldr     x0, [x1]
        add     x0, x0, x1
        ret
 0:     .quad   sym64_rel - .
-ENDPROC(relative_data64)
+SYM_FUNC_END(relative_data64)
 
-ENTRY(relative_data32)
+SYM_FUNC_START(relative_data32)
        adr     x1, 0f
        ldr     w0, [x1]
        add     x0, x0, x1
        ret
 0:     .long   sym64_rel - .
-ENDPROC(relative_data32)
+SYM_FUNC_END(relative_data32)
 
-ENTRY(relative_data16)
+SYM_FUNC_START(relative_data16)
        adr     x1, 0f
        ldrsh   w0, [x1]
        add     x0, x0, x1
        ret
 0:     .short  sym64_rel - ., 0
-ENDPROC(relative_data16)
+SYM_FUNC_END(relative_data16)
index c40ce496c78b0ea2445b8aadd94ceb9aaaae7452..542d6edc6806acaba0e57241c280336c9d90d462 100644 (file)
@@ -26,7 +26,7 @@
  * control_code_page, a special page which has been set up to be preserved
  * during the copy operation.
  */
-ENTRY(arm64_relocate_new_kernel)
+SYM_CODE_START(arm64_relocate_new_kernel)
 
        /* Setup the list loop variables. */
        mov     x18, x2                         /* x18 = dtb address */
@@ -111,7 +111,7 @@ ENTRY(arm64_relocate_new_kernel)
        mov     x3, xzr
        br      x17
 
-ENDPROC(arm64_relocate_new_kernel)
+SYM_CODE_END(arm64_relocate_new_kernel)
 
 .align 3       /* To keep the 64-bit values below naturally aligned. */
 
diff --git a/arch/arm64/kernel/scs.c b/arch/arm64/kernel/scs.c
new file mode 100644 (file)
index 0000000..e8f7ff4
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Shadow Call Stack support.
+ *
+ * Copyright (C) 2019 Google LLC
+ */
+
+#include <linux/percpu.h>
+#include <linux/scs.h>
+
+DEFINE_SCS(irq_shadow_call_stack);
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+DEFINE_SCS(sdei_shadow_call_stack_normal);
+DEFINE_SCS(sdei_shadow_call_stack_critical);
+#endif
index d6259dac62b62ceb2bfc8584bf3afc20fe0e20a1..dab88260b13739882df471d54917bd9c10c9ca9d 100644 (file)
@@ -95,19 +95,7 @@ static bool on_sdei_normal_stack(unsigned long sp, struct stack_info *info)
        unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_normal_ptr);
        unsigned long high = low + SDEI_STACK_SIZE;
 
-       if (!low)
-               return false;
-
-       if (sp < low || sp >= high)
-               return false;
-
-       if (info) {
-               info->low = low;
-               info->high = high;
-               info->type = STACK_TYPE_SDEI_NORMAL;
-       }
-
-       return true;
+       return on_stack(sp, low, high, STACK_TYPE_SDEI_NORMAL, info);
 }
 
 static bool on_sdei_critical_stack(unsigned long sp, struct stack_info *info)
@@ -115,19 +103,7 @@ static bool on_sdei_critical_stack(unsigned long sp, struct stack_info *info)
        unsigned long low = (unsigned long)raw_cpu_read(sdei_stack_critical_ptr);
        unsigned long high = low + SDEI_STACK_SIZE;
 
-       if (!low)
-               return false;
-
-       if (sp < low || sp >= high)
-               return false;
-
-       if (info) {
-               info->low = low;
-               info->high = high;
-               info->type = STACK_TYPE_SDEI_CRITICAL;
-       }
-
-       return true;
+       return on_stack(sp, low, high, STACK_TYPE_SDEI_CRITICAL, info);
 }
 
 bool _on_sdei_stack(unsigned long sp, struct stack_info *info)
@@ -251,22 +227,12 @@ asmlinkage __kprobes notrace unsigned long
 __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 {
        unsigned long ret;
-       bool do_nmi_exit = false;
 
-       /*
-        * nmi_enter() deals with printk() re-entrance and use of RCU when
-        * RCU believed this CPU was idle. Because critical events can
-        * interrupt normal events, we may already be in_nmi().
-        */
-       if (!in_nmi()) {
-               nmi_enter();
-               do_nmi_exit = true;
-       }
+       nmi_enter();
 
        ret = _sdei_handler(regs, arg);
 
-       if (do_nmi_exit)
-               nmi_exit();
+       nmi_exit();
 
        return ret;
 }
index 339882db5a9159bfd888d933dc5fff235408e475..801d56cdf70176530b6ce95e444714c362a3500b 100644 (file)
@@ -732,6 +732,22 @@ static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
        regs->regs[29] = (unsigned long)&user->next_frame->fp;
        regs->pc = (unsigned long)ka->sa.sa_handler;
 
+       /*
+        * Signal delivery is a (wacky) indirect function call in
+        * userspace, so simulate the same setting of BTYPE as a BLR
+        * <register containing the signal handler entry point>.
+        * Signal delivery to a location in a PROT_BTI guarded page
+        * that is not a function entry point will now trigger a
+        * SIGILL in userspace.
+        *
+        * If the signal handler entry point is not in a PROT_BTI
+        * guarded page, this is harmless.
+        */
+       if (system_supports_bti()) {
+               regs->pstate &= ~PSR_BTYPE_MASK;
+               regs->pstate |= PSR_BTYPE_C;
+       }
+
        if (ka->sa.sa_flags & SA_RESTORER)
                sigtramp = ka->sa.sa_restorer;
        else
index 7b2f2e650c44d01aacbafa4562a9d986f5316f4e..ba40d57757d63c10f671ba8f7e674d1e2430e161 100644 (file)
@@ -62,7 +62,7 @@
  *
  *  x0 = struct sleep_stack_data area
  */
-ENTRY(__cpu_suspend_enter)
+SYM_FUNC_START(__cpu_suspend_enter)
        stp     x29, lr, [x0, #SLEEP_STACK_DATA_CALLEE_REGS]
        stp     x19, x20, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+16]
        stp     x21, x22, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+32]
@@ -95,23 +95,22 @@ ENTRY(__cpu_suspend_enter)
        ldp     x29, lr, [sp], #16
        mov     x0, #1
        ret
-ENDPROC(__cpu_suspend_enter)
+SYM_FUNC_END(__cpu_suspend_enter)
 
        .pushsection ".idmap.text", "awx"
-ENTRY(cpu_resume)
+SYM_CODE_START(cpu_resume)
        bl      el2_setup               // if in EL2 drop to EL1 cleanly
-       mov     x0, #ARM64_CPU_RUNTIME
        bl      __cpu_setup
        /* enable the MMU early - so we can access sleep_save_stash by va */
        adrp    x1, swapper_pg_dir
        bl      __enable_mmu
        ldr     x8, =_cpu_resume
        br      x8
-ENDPROC(cpu_resume)
+SYM_CODE_END(cpu_resume)
        .ltorg
        .popsection
 
-ENTRY(_cpu_resume)
+SYM_FUNC_START(_cpu_resume)
        mrs     x1, mpidr_el1
        adr_l   x8, mpidr_hash          // x8 = struct mpidr_hash virt address
 
@@ -147,4 +146,4 @@ ENTRY(_cpu_resume)
        ldp     x29, lr, [x29]
        mov     x0, #0
        ret
-ENDPROC(_cpu_resume)
+SYM_FUNC_END(_cpu_resume)
index 54655273d1e0ba9e619953e9ce4d3bbfd73426da..1f93809528a4f19fde0a4964fc4cfd1fdc8b7fa7 100644 (file)
@@ -30,9 +30,9 @@
  *               unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
  *               struct arm_smccc_quirk *quirk)
  */
-ENTRY(__arm_smccc_smc)
+SYM_FUNC_START(__arm_smccc_smc)
        SMCCC   smc
-ENDPROC(__arm_smccc_smc)
+SYM_FUNC_END(__arm_smccc_smc)
 EXPORT_SYMBOL(__arm_smccc_smc)
 
 /*
@@ -41,7 +41,7 @@ EXPORT_SYMBOL(__arm_smccc_smc)
  *               unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
  *               struct arm_smccc_quirk *quirk)
  */
-ENTRY(__arm_smccc_hvc)
+SYM_FUNC_START(__arm_smccc_hvc)
        SMCCC   hvc
-ENDPROC(__arm_smccc_hvc)
+SYM_FUNC_END(__arm_smccc_hvc)
 EXPORT_SYMBOL(__arm_smccc_hvc)
index 061f60fe452f773e233c0bffc68b0c614db905a4..04b1ca0d7aba3957da7b100123eda635b6637f9f 100644 (file)
@@ -65,7 +65,7 @@ EXPORT_PER_CPU_SYMBOL(cpu_number);
  */
 struct secondary_data secondary_data;
 /* Number of CPUs which aren't online, but looping in kernel text. */
-int cpus_stuck_in_kernel;
+static int cpus_stuck_in_kernel;
 
 enum ipi_msg_type {
        IPI_RESCHEDULE,
@@ -114,10 +114,6 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
         */
        secondary_data.task = idle;
        secondary_data.stack = task_stack_page(idle) + THREAD_SIZE;
-#if defined(CONFIG_ARM64_PTR_AUTH)
-       secondary_data.ptrauth_key.apia.lo = idle->thread.keys_kernel.apia.lo;
-       secondary_data.ptrauth_key.apia.hi = idle->thread.keys_kernel.apia.hi;
-#endif
        update_cpu_boot_status(CPU_MMU_OFF);
        __flush_dcache_area(&secondary_data, sizeof(secondary_data));
 
@@ -140,10 +136,6 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
        pr_crit("CPU%u: failed to come online\n", cpu);
        secondary_data.task = NULL;
        secondary_data.stack = NULL;
-#if defined(CONFIG_ARM64_PTR_AUTH)
-       secondary_data.ptrauth_key.apia.lo = 0;
-       secondary_data.ptrauth_key.apia.hi = 0;
-#endif
        __flush_dcache_area(&secondary_data, sizeof(secondary_data));
        status = READ_ONCE(secondary_data.status);
        if (status == CPU_MMU_OFF)
@@ -176,7 +168,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
                panic("CPU%u detected unsupported configuration\n", cpu);
        }
 
-       return ret;
+       return -EIO;
 }
 
 static void init_gic_priority_masking(void)
index a12c0c88d3457357de64c32863690726639112f2..5f5b868292f52233f7d2b7f21f1f3d8d1ac5bdb6 100644 (file)
@@ -98,6 +98,24 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
        regs->orig_x0 = regs->regs[0];
        regs->syscallno = scno;
 
+       /*
+        * BTI note:
+        * The architecture does not guarantee that SPSR.BTYPE is zero
+        * on taking an SVC, so we could return to userspace with a
+        * non-zero BTYPE after the syscall.
+        *
+        * This shouldn't matter except when userspace is explicitly
+        * doing something stupid, such as setting PROT_BTI on a page
+        * that lacks conforming BTI/PACIxSP instructions, falling
+        * through from one executable page to another with differing
+        * PROT_BTI, or messing with BTYPE via ptrace: in such cases,
+        * userspace should not be surprised if a SIGILL occurs on
+        * syscall return.
+        *
+        * So, don't touch regs->pstate & PSR_BTYPE_MASK here.
+        * (Similarly for HVC and SMC elsewhere.)
+        */
+
        cortex_a76_erratum_1463225_svc_handler();
        local_daif_restore(DAIF_PROCCTX);
        user_exit();
index cf402be5c573ff882797422ee02bc913d7c17f24..d332590f59782c9ede3aa955fe1f78ed62a1256b 100644 (file)
@@ -272,6 +272,61 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
        }
 }
 
+#ifdef CONFIG_COMPAT
+#define PSTATE_IT_1_0_SHIFT    25
+#define PSTATE_IT_1_0_MASK     (0x3 << PSTATE_IT_1_0_SHIFT)
+#define PSTATE_IT_7_2_SHIFT    10
+#define PSTATE_IT_7_2_MASK     (0x3f << PSTATE_IT_7_2_SHIFT)
+
+static u32 compat_get_it_state(struct pt_regs *regs)
+{
+       u32 it, pstate = regs->pstate;
+
+       it  = (pstate & PSTATE_IT_1_0_MASK) >> PSTATE_IT_1_0_SHIFT;
+       it |= ((pstate & PSTATE_IT_7_2_MASK) >> PSTATE_IT_7_2_SHIFT) << 2;
+
+       return it;
+}
+
+static void compat_set_it_state(struct pt_regs *regs, u32 it)
+{
+       u32 pstate_it;
+
+       pstate_it  = (it << PSTATE_IT_1_0_SHIFT) & PSTATE_IT_1_0_MASK;
+       pstate_it |= ((it >> 2) << PSTATE_IT_7_2_SHIFT) & PSTATE_IT_7_2_MASK;
+
+       regs->pstate &= ~PSR_AA32_IT_MASK;
+       regs->pstate |= pstate_it;
+}
+
+static void advance_itstate(struct pt_regs *regs)
+{
+       u32 it;
+
+       /* ARM mode */
+       if (!(regs->pstate & PSR_AA32_T_BIT) ||
+           !(regs->pstate & PSR_AA32_IT_MASK))
+               return;
+
+       it  = compat_get_it_state(regs);
+
+       /*
+        * If this is the last instruction of the block, wipe the IT
+        * state. Otherwise advance it.
+        */
+       if (!(it & 7))
+               it = 0;
+       else
+               it = (it & 0xe0) | ((it << 1) & 0x1f);
+
+       compat_set_it_state(regs, it);
+}
+#else
+static void advance_itstate(struct pt_regs *regs)
+{
+}
+#endif
+
 void arm64_skip_faulting_instruction(struct pt_regs *regs, unsigned long size)
 {
        regs->pc += size;
@@ -282,6 +337,11 @@ void arm64_skip_faulting_instruction(struct pt_regs *regs, unsigned long size)
         */
        if (user_mode(regs))
                user_fastforward_single_step(current);
+
+       if (compat_user_mode(regs))
+               advance_itstate(regs);
+       else
+               regs->pstate &= ~PSR_BTYPE_MASK;
 }
 
 static LIST_HEAD(undef_hook);
@@ -411,6 +471,13 @@ void do_undefinstr(struct pt_regs *regs)
 }
 NOKPROBE_SYMBOL(do_undefinstr);
 
+void do_bti(struct pt_regs *regs)
+{
+       BUG_ON(!user_mode(regs));
+       force_signal_inject(SIGILL, ILL_ILLOPC, regs->pc);
+}
+NOKPROBE_SYMBOL(do_bti);
+
 #define __user_cache_maint(insn, address, res)                 \
        if (address >= user_addr_max()) {                       \
                res = -EFAULT;                                  \
@@ -566,34 +633,7 @@ static const struct sys64_hook sys64_hooks[] = {
        {},
 };
 
-
 #ifdef CONFIG_COMPAT
-#define PSTATE_IT_1_0_SHIFT    25
-#define PSTATE_IT_1_0_MASK     (0x3 << PSTATE_IT_1_0_SHIFT)
-#define PSTATE_IT_7_2_SHIFT    10
-#define PSTATE_IT_7_2_MASK     (0x3f << PSTATE_IT_7_2_SHIFT)
-
-static u32 compat_get_it_state(struct pt_regs *regs)
-{
-       u32 it, pstate = regs->pstate;
-
-       it  = (pstate & PSTATE_IT_1_0_MASK) >> PSTATE_IT_1_0_SHIFT;
-       it |= ((pstate & PSTATE_IT_7_2_MASK) >> PSTATE_IT_7_2_SHIFT) << 2;
-
-       return it;
-}
-
-static void compat_set_it_state(struct pt_regs *regs, u32 it)
-{
-       u32 pstate_it;
-
-       pstate_it  = (it << PSTATE_IT_1_0_SHIFT) & PSTATE_IT_1_0_MASK;
-       pstate_it |= ((it >> 2) << PSTATE_IT_7_2_SHIFT) & PSTATE_IT_7_2_MASK;
-
-       regs->pstate &= ~PSR_AA32_IT_MASK;
-       regs->pstate |= pstate_it;
-}
-
 static bool cp15_cond_valid(unsigned int esr, struct pt_regs *regs)
 {
        int cond;
@@ -614,42 +654,12 @@ static bool cp15_cond_valid(unsigned int esr, struct pt_regs *regs)
        return aarch32_opcode_cond_checks[cond](regs->pstate);
 }
 
-static void advance_itstate(struct pt_regs *regs)
-{
-       u32 it;
-
-       /* ARM mode */
-       if (!(regs->pstate & PSR_AA32_T_BIT) ||
-           !(regs->pstate & PSR_AA32_IT_MASK))
-               return;
-
-       it  = compat_get_it_state(regs);
-
-       /*
-        * If this is the last instruction of the block, wipe the IT
-        * state. Otherwise advance it.
-        */
-       if (!(it & 7))
-               it = 0;
-       else
-               it = (it & 0xe0) | ((it << 1) & 0x1f);
-
-       compat_set_it_state(regs, it);
-}
-
-static void arm64_compat_skip_faulting_instruction(struct pt_regs *regs,
-                                                  unsigned int sz)
-{
-       advance_itstate(regs);
-       arm64_skip_faulting_instruction(regs, sz);
-}
-
 static void compat_cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
 {
        int reg = (esr & ESR_ELx_CP15_32_ISS_RT_MASK) >> ESR_ELx_CP15_32_ISS_RT_SHIFT;
 
        pt_regs_write_reg(regs, reg, arch_timer_get_rate());
-       arm64_compat_skip_faulting_instruction(regs, 4);
+       arm64_skip_faulting_instruction(regs, 4);
 }
 
 static const struct sys64_hook cp15_32_hooks[] = {
@@ -669,7 +679,7 @@ static void compat_cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
 
        pt_regs_write_reg(regs, rt, lower_32_bits(val));
        pt_regs_write_reg(regs, rt2, upper_32_bits(val));
-       arm64_compat_skip_faulting_instruction(regs, 4);
+       arm64_skip_faulting_instruction(regs, 4);
 }
 
 static const struct sys64_hook cp15_64_hooks[] = {
@@ -690,7 +700,7 @@ void do_cp15instr(unsigned int esr, struct pt_regs *regs)
                 * There is no T16 variant of a CP access, so we
                 * always advance PC by 4 bytes.
                 */
-               arm64_compat_skip_faulting_instruction(regs, 4);
+               arm64_skip_faulting_instruction(regs, 4);
                return;
        }
 
@@ -753,6 +763,7 @@ static const char *esr_class_str[] = {
        [ESR_ELx_EC_CP10_ID]            = "CP10 MRC/VMRS",
        [ESR_ELx_EC_PAC]                = "PAC",
        [ESR_ELx_EC_CP14_64]            = "CP14 MCRR/MRRC",
+       [ESR_ELx_EC_BTI]                = "BTI",
        [ESR_ELx_EC_ILL]                = "PSTATE.IL",
        [ESR_ELx_EC_SVC32]              = "SVC (AArch32)",
        [ESR_ELx_EC_HVC32]              = "HVC (AArch32)",
@@ -906,17 +917,13 @@ bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr)
 
 asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr)
 {
-       const bool was_in_nmi = in_nmi();
-
-       if (!was_in_nmi)
-               nmi_enter();
+       nmi_enter();
 
        /* non-RAS errors are not containable */
        if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr))
                arm64_serror_panic(regs, esr);
 
-       if (!was_in_nmi)
-               nmi_exit();
+       nmi_exit();
 }
 
 asmlinkage void enter_from_user_mode(void)
@@ -1047,11 +1054,11 @@ int __init early_brk64(unsigned long addr, unsigned int esr,
        return bug_handler(regs, esr) != DBG_HOOK_HANDLED;
 }
 
-/* This registration must happen early, before debug_traps_init(). */
 void __init trap_init(void)
 {
        register_kernel_break_hook(&bug_break_hook);
 #ifdef CONFIG_KASAN_SW_TAGS
        register_kernel_break_hook(&kasan_break_hook);
 #endif
+       debug_traps_init();
 }
index 033a48f30dbb801a58dfe64998fe102eb663aecc..d51a898fd60f2bb634b7c9baeedc2b6010945a50 100644 (file)
@@ -33,20 +33,14 @@ extern char vdso_start[], vdso_end[];
 extern char vdso32_start[], vdso32_end[];
 #endif /* CONFIG_COMPAT_VDSO */
 
-/* vdso_lookup arch_index */
-enum arch_vdso_type {
-       ARM64_VDSO = 0,
+enum vdso_abi {
+       VDSO_ABI_AA64,
 #ifdef CONFIG_COMPAT_VDSO
-       ARM64_VDSO32 = 1,
+       VDSO_ABI_AA32,
 #endif /* CONFIG_COMPAT_VDSO */
 };
-#ifdef CONFIG_COMPAT_VDSO
-#define VDSO_TYPES             (ARM64_VDSO32 + 1)
-#else
-#define VDSO_TYPES             (ARM64_VDSO + 1)
-#endif /* CONFIG_COMPAT_VDSO */
 
-struct __vdso_abi {
+struct vdso_abi_info {
        const char *name;
        const char *vdso_code_start;
        const char *vdso_code_end;
@@ -57,14 +51,14 @@ struct __vdso_abi {
        struct vm_special_mapping *cm;
 };
 
-static struct __vdso_abi vdso_lookup[VDSO_TYPES] __ro_after_init = {
-       {
+static struct vdso_abi_info vdso_info[] __ro_after_init = {
+       [VDSO_ABI_AA64] = {
                .name = "vdso",
                .vdso_code_start = vdso_start,
                .vdso_code_end = vdso_end,
        },
 #ifdef CONFIG_COMPAT_VDSO
-       {
+       [VDSO_ABI_AA32] = {
                .name = "vdso32",
                .vdso_code_start = vdso32_start,
                .vdso_code_end = vdso32_end,
@@ -81,13 +75,13 @@ static union {
 } vdso_data_store __page_aligned_data;
 struct vdso_data *vdso_data = vdso_data_store.data;
 
-static int __vdso_remap(enum arch_vdso_type arch_index,
+static int __vdso_remap(enum vdso_abi abi,
                        const struct vm_special_mapping *sm,
                        struct vm_area_struct *new_vma)
 {
        unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
-       unsigned long vdso_size = vdso_lookup[arch_index].vdso_code_end -
-                                 vdso_lookup[arch_index].vdso_code_start;
+       unsigned long vdso_size = vdso_info[abi].vdso_code_end -
+                                 vdso_info[abi].vdso_code_start;
 
        if (vdso_size != new_size)
                return -EINVAL;
@@ -97,24 +91,24 @@ static int __vdso_remap(enum arch_vdso_type arch_index,
        return 0;
 }
 
-static int __vdso_init(enum arch_vdso_type arch_index)
+static int __vdso_init(enum vdso_abi abi)
 {
        int i;
        struct page **vdso_pagelist;
        unsigned long pfn;
 
-       if (memcmp(vdso_lookup[arch_index].vdso_code_start, "\177ELF", 4)) {
+       if (memcmp(vdso_info[abi].vdso_code_start, "\177ELF", 4)) {
                pr_err("vDSO is not a valid ELF object!\n");
                return -EINVAL;
        }
 
-       vdso_lookup[arch_index].vdso_pages = (
-                       vdso_lookup[arch_index].vdso_code_end -
-                       vdso_lookup[arch_index].vdso_code_start) >>
+       vdso_info[abi].vdso_pages = (
+                       vdso_info[abi].vdso_code_end -
+                       vdso_info[abi].vdso_code_start) >>
                        PAGE_SHIFT;
 
        /* Allocate the vDSO pagelist, plus a page for the data. */
-       vdso_pagelist = kcalloc(vdso_lookup[arch_index].vdso_pages + 1,
+       vdso_pagelist = kcalloc(vdso_info[abi].vdso_pages + 1,
                                sizeof(struct page *),
                                GFP_KERNEL);
        if (vdso_pagelist == NULL)
@@ -125,26 +119,27 @@ static int __vdso_init(enum arch_vdso_type arch_index)
 
 
        /* Grab the vDSO code pages. */
-       pfn = sym_to_pfn(vdso_lookup[arch_index].vdso_code_start);
+       pfn = sym_to_pfn(vdso_info[abi].vdso_code_start);
 
-       for (i = 0; i < vdso_lookup[arch_index].vdso_pages; i++)
+       for (i = 0; i < vdso_info[abi].vdso_pages; i++)
                vdso_pagelist[i + 1] = pfn_to_page(pfn + i);
 
-       vdso_lookup[arch_index].dm->pages = &vdso_pagelist[0];
-       vdso_lookup[arch_index].cm->pages = &vdso_pagelist[1];
+       vdso_info[abi].dm->pages = &vdso_pagelist[0];
+       vdso_info[abi].cm->pages = &vdso_pagelist[1];
 
        return 0;
 }
 
-static int __setup_additional_pages(enum arch_vdso_type arch_index,
+static int __setup_additional_pages(enum vdso_abi abi,
                                    struct mm_struct *mm,
                                    struct linux_binprm *bprm,
                                    int uses_interp)
 {
        unsigned long vdso_base, vdso_text_len, vdso_mapping_len;
+       unsigned long gp_flags = 0;
        void *ret;
 
-       vdso_text_len = vdso_lookup[arch_index].vdso_pages << PAGE_SHIFT;
+       vdso_text_len = vdso_info[abi].vdso_pages << PAGE_SHIFT;
        /* Be sure to map the data page */
        vdso_mapping_len = vdso_text_len + PAGE_SIZE;
 
@@ -156,16 +151,19 @@ static int __setup_additional_pages(enum arch_vdso_type arch_index,
 
        ret = _install_special_mapping(mm, vdso_base, PAGE_SIZE,
                                       VM_READ|VM_MAYREAD,
-                                      vdso_lookup[arch_index].dm);
+                                      vdso_info[abi].dm);
        if (IS_ERR(ret))
                goto up_fail;
 
+       if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) && system_supports_bti())
+               gp_flags = VM_ARM64_BTI;
+
        vdso_base += PAGE_SIZE;
        mm->context.vdso = (void *)vdso_base;
        ret = _install_special_mapping(mm, vdso_base, vdso_text_len,
-                                      VM_READ|VM_EXEC|
+                                      VM_READ|VM_EXEC|gp_flags|
                                       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
-                                      vdso_lookup[arch_index].cm);
+                                      vdso_info[abi].cm);
        if (IS_ERR(ret))
                goto up_fail;
 
@@ -184,46 +182,42 @@ up_fail:
 static int aarch32_vdso_mremap(const struct vm_special_mapping *sm,
                struct vm_area_struct *new_vma)
 {
-       return __vdso_remap(ARM64_VDSO32, sm, new_vma);
+       return __vdso_remap(VDSO_ABI_AA32, sm, new_vma);
 }
 #endif /* CONFIG_COMPAT_VDSO */
 
-/*
- * aarch32_vdso_pages:
- * 0 - kuser helpers
- * 1 - sigreturn code
- * or (CONFIG_COMPAT_VDSO):
- * 0 - kuser helpers
- * 1 - vdso data
- * 2 - vdso code
- */
-#define C_VECTORS      0
+enum aarch32_map {
+       AA32_MAP_VECTORS, /* kuser helpers */
 #ifdef CONFIG_COMPAT_VDSO
-#define C_VVAR         1
-#define C_VDSO         2
-#define C_PAGES                (C_VDSO + 1)
+       AA32_MAP_VVAR,
+       AA32_MAP_VDSO,
 #else
-#define C_SIGPAGE      1
-#define C_PAGES                (C_SIGPAGE + 1)
-#endif /* CONFIG_COMPAT_VDSO */
-static struct page *aarch32_vdso_pages[C_PAGES] __ro_after_init;
-static struct vm_special_mapping aarch32_vdso_spec[C_PAGES] = {
-       {
+       AA32_MAP_SIGPAGE
+#endif
+};
+
+static struct page *aarch32_vectors_page __ro_after_init;
+#ifndef CONFIG_COMPAT_VDSO
+static struct page *aarch32_sig_page __ro_after_init;
+#endif
+
+static struct vm_special_mapping aarch32_vdso_maps[] = {
+       [AA32_MAP_VECTORS] = {
                .name   = "[vectors]", /* ABI */
-               .pages  = &aarch32_vdso_pages[C_VECTORS],
+               .pages  = &aarch32_vectors_page,
        },
 #ifdef CONFIG_COMPAT_VDSO
-       {
+       [AA32_MAP_VVAR] = {
                .name = "[vvar]",
        },
-       {
+       [AA32_MAP_VDSO] = {
                .name = "[vdso]",
                .mremap = aarch32_vdso_mremap,
        },
 #else
-       {
+       [AA32_MAP_SIGPAGE] = {
                .name   = "[sigpage]", /* ABI */
-               .pages  = &aarch32_vdso_pages[C_SIGPAGE],
+               .pages  = &aarch32_sig_page,
        },
 #endif /* CONFIG_COMPAT_VDSO */
 };
@@ -243,8 +237,8 @@ static int aarch32_alloc_kuser_vdso_page(void)
 
        memcpy((void *)(vdso_page + 0x1000 - kuser_sz), __kuser_helper_start,
               kuser_sz);
-       aarch32_vdso_pages[C_VECTORS] = virt_to_page(vdso_page);
-       flush_dcache_page(aarch32_vdso_pages[C_VECTORS]);
+       aarch32_vectors_page = virt_to_page(vdso_page);
+       flush_dcache_page(aarch32_vectors_page);
        return 0;
 }
 
@@ -253,10 +247,10 @@ static int __aarch32_alloc_vdso_pages(void)
 {
        int ret;
 
-       vdso_lookup[ARM64_VDSO32].dm = &aarch32_vdso_spec[C_VVAR];
-       vdso_lookup[ARM64_VDSO32].cm = &aarch32_vdso_spec[C_VDSO];
+       vdso_info[VDSO_ABI_AA32].dm = &aarch32_vdso_maps[AA32_MAP_VVAR];
+       vdso_info[VDSO_ABI_AA32].cm = &aarch32_vdso_maps[AA32_MAP_VDSO];
 
-       ret = __vdso_init(ARM64_VDSO32);
+       ret = __vdso_init(VDSO_ABI_AA32);
        if (ret)
                return ret;
 
@@ -275,8 +269,8 @@ static int __aarch32_alloc_vdso_pages(void)
                return -ENOMEM;
 
        memcpy((void *)sigpage, __aarch32_sigret_code_start, sigret_sz);
-       aarch32_vdso_pages[C_SIGPAGE] = virt_to_page(sigpage);
-       flush_dcache_page(aarch32_vdso_pages[C_SIGPAGE]);
+       aarch32_sig_page = virt_to_page(sigpage);
+       flush_dcache_page(aarch32_sig_page);
 
        ret = aarch32_alloc_kuser_vdso_page();
        if (ret)
@@ -306,7 +300,7 @@ static int aarch32_kuser_helpers_setup(struct mm_struct *mm)
        ret = _install_special_mapping(mm, AARCH32_VECTORS_BASE, PAGE_SIZE,
                                       VM_READ | VM_EXEC |
                                       VM_MAYREAD | VM_MAYEXEC,
-                                      &aarch32_vdso_spec[C_VECTORS]);
+                                      &aarch32_vdso_maps[AA32_MAP_VECTORS]);
 
        return PTR_ERR_OR_ZERO(ret);
 }
@@ -330,7 +324,7 @@ static int aarch32_sigreturn_setup(struct mm_struct *mm)
        ret = _install_special_mapping(mm, addr, PAGE_SIZE,
                                       VM_READ | VM_EXEC | VM_MAYREAD |
                                       VM_MAYWRITE | VM_MAYEXEC,
-                                      &aarch32_vdso_spec[C_SIGPAGE]);
+                                      &aarch32_vdso_maps[AA32_MAP_SIGPAGE]);
        if (IS_ERR(ret))
                goto out;
 
@@ -354,7 +348,7 @@ int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
                goto out;
 
 #ifdef CONFIG_COMPAT_VDSO
-       ret = __setup_additional_pages(ARM64_VDSO32,
+       ret = __setup_additional_pages(VDSO_ABI_AA32,
                                       mm,
                                       bprm,
                                       uses_interp);
@@ -371,22 +365,19 @@ out:
 static int vdso_mremap(const struct vm_special_mapping *sm,
                struct vm_area_struct *new_vma)
 {
-       return __vdso_remap(ARM64_VDSO, sm, new_vma);
+       return __vdso_remap(VDSO_ABI_AA64, sm, new_vma);
 }
 
-/*
- * aarch64_vdso_pages:
- * 0 - vvar
- * 1 - vdso
- */
-#define A_VVAR         0
-#define A_VDSO         1
-#define A_PAGES                (A_VDSO + 1)
-static struct vm_special_mapping vdso_spec[A_PAGES] __ro_after_init = {
-       {
+enum aarch64_map {
+       AA64_MAP_VVAR,
+       AA64_MAP_VDSO,
+};
+
+static struct vm_special_mapping aarch64_vdso_maps[] __ro_after_init = {
+       [AA64_MAP_VVAR] = {
                .name   = "[vvar]",
        },
-       {
+       [AA64_MAP_VDSO] = {
                .name   = "[vdso]",
                .mremap = vdso_mremap,
        },
@@ -394,10 +385,10 @@ static struct vm_special_mapping vdso_spec[A_PAGES] __ro_after_init = {
 
 static int __init vdso_init(void)
 {
-       vdso_lookup[ARM64_VDSO].dm = &vdso_spec[A_VVAR];
-       vdso_lookup[ARM64_VDSO].cm = &vdso_spec[A_VDSO];
+       vdso_info[VDSO_ABI_AA64].dm = &aarch64_vdso_maps[AA64_MAP_VVAR];
+       vdso_info[VDSO_ABI_AA64].cm = &aarch64_vdso_maps[AA64_MAP_VDSO];
 
-       return __vdso_init(ARM64_VDSO);
+       return __vdso_init(VDSO_ABI_AA64);
 }
 arch_initcall(vdso_init);
 
@@ -410,7 +401,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
        if (down_write_killable(&mm->mmap_sem))
                return -EINTR;
 
-       ret = __setup_additional_pages(ARM64_VDSO,
+       ret = __setup_additional_pages(VDSO_ABI_AA64,
                                       mm,
                                       bprm,
                                       uses_interp);
index 3862cad2410cfab14a3d028882b7e856cffb344f..556d424c6f52f18b7fe83ef7e177937635196dd2 100644 (file)
@@ -17,15 +17,19 @@ obj-vdso := vgettimeofday.o note.o sigreturn.o
 targets := $(obj-vdso) vdso.so vdso.so.dbg
 obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
 
+btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti
+
+# -Bsymbolic has been added for consistency with arm, the compat vDSO and
+# potential future proofing if we end up with internal calls to the exported
+# routines, as x86 does (see 6f121e548f83 ("x86, vdso: Reimplement vdso.so
+# preparation in build-time C")).
 ldflags-y := -shared -nostdlib -soname=linux-vdso.so.1 --hash-style=sysv \
-               --build-id -n -T
+               -Bsymbolic --eh-frame-hdr --build-id -n $(btildflags-y) -T
 
 ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18
 ccflags-y += -DDISABLE_BRANCH_PROFILING
 
-VDSO_LDFLAGS := -Bsymbolic
-
-CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os
+CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS)
 KBUILD_CFLAGS                  += $(DISABLE_LTO)
 KASAN_SANITIZE                 := n
 UBSAN_SANITIZE                 := n
index 0ce6ec75a525298b44de658c265f4762ecf4dd00..3d4e82290c80476c0e2ef39b9cd5cbc07415a2bc 100644 (file)
 #include <linux/version.h>
 #include <linux/elfnote.h>
 #include <linux/build-salt.h>
+#include <asm/assembler.h>
 
 ELFNOTE_START(Linux, 0, "a")
        .long LINUX_VERSION_CODE
 ELFNOTE_END
 
 BUILD_SALT
+
+emit_aarch64_feature_1_and
index 12324863d5c22819d8f31e3cec8786fb953bdcbc..620a3ef837b7c9e7e819105601b18558d14a4083 100644 (file)
@@ -1,7 +1,11 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Sigreturn trampoline for returning from a signal when the SA_RESTORER
- * flag is not set.
+ * flag is not set. It serves primarily as a hall of shame for crappy
+ * unwinders and features an exciting but mysterious NOP instruction.
+ *
+ * It's also fragile as hell, so please think twice before changing anything
+ * in here.
  *
  * Copyright (C) 2012 ARM Limited
  *
  */
 
 #include <linux/linkage.h>
+#include <asm/assembler.h>
 #include <asm/unistd.h>
 
        .text
 
-       nop
-SYM_FUNC_START(__kernel_rt_sigreturn)
+/* Ensure that the mysterious NOP can be associated with a function. */
        .cfi_startproc
+
+/*
+ * .cfi_signal_frame causes the corresponding Frame Description Entry in the
+ * .eh_frame section to be annotated as a signal frame. This allows DWARF
+ * unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo(), which permits
+ * unwinding out of the signal trampoline without the need for the mysterious
+ * NOP.
+ */
        .cfi_signal_frame
-       .cfi_def_cfa    x29, 0
-       .cfi_offset     x29, 0 * 8
-       .cfi_offset     x30, 1 * 8
+
+/*
+ * Tell the unwinder where to locate the frame record linking back to the
+ * interrupted context. We don't provide unwind info for registers other
+ * than the frame pointer and the link register here; in practice, this
+ * is sufficient for unwinding in C/C++ based runtimes and the values in
+ * the sigcontext may have been modified by this point anyway. Debuggers
+ * already have baked-in strategies for attempting to unwind out of signals.
+ */
+       .cfi_def_cfa    x29, 0
+       .cfi_offset     x29, 0 * 8
+       .cfi_offset     x30, 1 * 8
+
+/*
+ * This mysterious NOP is required for some unwinders (e.g. libc++) that
+ * unconditionally subtract one from the result of _Unwind_GetIP() in order to
+ * identify the calling function.
+ * Hack borrowed from arch/powerpc/kernel/vdso64/sigtramp.S.
+ */
+       nop     // Mysterious NOP
+
+/*
+ * GDB relies on being able to identify the sigreturn instruction sequence to
+ * unwind from signal handlers. We cannot, therefore, use SYM_FUNC_START()
+ * here, as it will emit a BTI C instruction and break the unwinder. Thankfully,
+ * this function is only ever called from a RET and so omitting the landing pad
+ * is perfectly fine.
+ */
+SYM_CODE_START(__kernel_rt_sigreturn)
        mov     x8, #__NR_rt_sigreturn
        svc     #0
        .cfi_endproc
-SYM_FUNC_END(__kernel_rt_sigreturn)
+SYM_CODE_END(__kernel_rt_sigreturn)
+
+emit_aarch64_feature_1_and
index d1414fee5274b7fbe76933672aeb99eb3884df0d..c4b1990bf2be09eedf4968da006fc73402ecd00b 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/init.h>
 #include <linux/linkage.h>
 #include <linux/const.h>
+#include <asm/assembler.h>
 #include <asm/page.h>
 
        .globl vdso_start, vdso_end
@@ -19,3 +20,5 @@ vdso_start:
 vdso_end:
 
        .previous
+
+emit_aarch64_feature_1_and
index 6205249696961f7df739a9a000ff5ac29ea145e3..b0091064c3d652f18d433b95e63ab287520a8183 100644 (file)
@@ -3,6 +3,9 @@
  * This file provides both A32 and T32 versions, in accordance with the
  * arm sigreturn code.
  *
+ * Please read the comments in arch/arm64/kernel/vdso/sigreturn.S to
+ * understand some of the craziness in here.
+ *
  * Copyright (C) 2018 ARM Limited
  */
 
        .save {r0-r15}
        .pad #COMPAT_SIGFRAME_REGS_OFFSET
        nop
-SYM_FUNC_START(__kernel_sigreturn_arm)
+SYM_CODE_START(__kernel_sigreturn_arm)
        mov r7, #__NR_compat_sigreturn
        svc #0
        .fnend
-SYM_FUNC_END(__kernel_sigreturn_arm)
+SYM_CODE_END(__kernel_sigreturn_arm)
 
        .fnstart
        .save {r0-r15}
        .pad #COMPAT_RT_SIGFRAME_REGS_OFFSET
        nop
-SYM_FUNC_START(__kernel_rt_sigreturn_arm)
+SYM_CODE_START(__kernel_rt_sigreturn_arm)
        mov r7, #__NR_compat_rt_sigreturn
        svc #0
        .fnend
-SYM_FUNC_END(__kernel_rt_sigreturn_arm)
+SYM_CODE_END(__kernel_rt_sigreturn_arm)
 
        .thumb
        .fnstart
        .save {r0-r15}
        .pad #COMPAT_SIGFRAME_REGS_OFFSET
        nop
-SYM_FUNC_START(__kernel_sigreturn_thumb)
+SYM_CODE_START(__kernel_sigreturn_thumb)
        mov r7, #__NR_compat_sigreturn
        svc #0
        .fnend
-SYM_FUNC_END(__kernel_sigreturn_thumb)
+SYM_CODE_END(__kernel_sigreturn_thumb)
 
        .fnstart
        .save {r0-r15}
        .pad #COMPAT_RT_SIGFRAME_REGS_OFFSET
        nop
-SYM_FUNC_START(__kernel_rt_sigreturn_thumb)
+SYM_CODE_START(__kernel_rt_sigreturn_thumb)
        mov r7, #__NR_compat_rt_sigreturn
        svc #0
        .fnend
-SYM_FUNC_END(__kernel_rt_sigreturn_thumb)
+SYM_CODE_END(__kernel_rt_sigreturn_thumb)
index 497f9675071d428bff29deb8fa009aeaa600e663..3be6321776310c94c88fb2656c173b7e374139a9 100644 (file)
 
 #include "image.h"
 
-/* .exit.text needed in case of alternative patching */
-#define ARM_EXIT_KEEP(x)       x
-#define ARM_EXIT_DISCARD(x)
-
 OUTPUT_ARCH(aarch64)
 ENTRY(_text)
 
@@ -72,8 +68,8 @@ jiffies = jiffies_64;
 
 /*
  * The size of the PE/COFF section that covers the kernel image, which
- * runs from stext to _edata, must be a round multiple of the PE/COFF
- * FileAlignment, which we set to its minimum value of 0x200. 'stext'
+ * runs from _stext to _edata, must be a round multiple of the PE/COFF
+ * FileAlignment, which we set to its minimum value of 0x200. '_stext'
  * itself is 4 KB aligned, so padding out _edata to a 0x200 aligned
  * boundary should be sufficient.
  */
@@ -95,8 +91,6 @@ SECTIONS
         * order of matching.
         */
        /DISCARD/ : {
-               ARM_EXIT_DISCARD(EXIT_TEXT)
-               ARM_EXIT_DISCARD(EXIT_DATA)
                EXIT_CALL
                *(.discard)
                *(.discard.*)
@@ -139,6 +133,7 @@ SECTIONS
 
        idmap_pg_dir = .;
        . += IDMAP_DIR_SIZE;
+       idmap_pg_end = .;
 
 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0
        tramp_pg_dir = .;
@@ -161,7 +156,7 @@ SECTIONS
 
        __exittext_begin = .;
        .exit.text : {
-               ARM_EXIT_KEEP(EXIT_TEXT)
+               EXIT_TEXT
        }
        __exittext_end = .;
 
@@ -175,7 +170,7 @@ SECTIONS
                *(.altinstr_replacement)
        }
 
-       . = ALIGN(PAGE_SIZE);
+       . = ALIGN(SEGMENT_ALIGN);
        __inittext_end = .;
        __initdata_begin = .;
 
@@ -188,7 +183,7 @@ SECTIONS
                *(.init.rodata.* .init.bss)     /* from the EFI stub */
        }
        .exit.data : {
-               ARM_EXIT_KEEP(EXIT_DATA)
+               EXIT_DATA
        }
 
        PERCPU_SECTION(L1_CACHE_BYTES)
@@ -246,6 +241,7 @@ SECTIONS
        . += INIT_DIR_SIZE;
        init_pg_end = .;
 
+       . = ALIGN(SEGMENT_ALIGN);
        __pecoff_data_size = ABSOLUTE(. - __initdata_begin);
        _end = .;
 
index 8a1e81a400e0f0257c3e6a45775a7b2c16b3b15b..1336e6f0acdf1b84d8375ef131f29212a75b7c7e 100644 (file)
@@ -138,7 +138,7 @@ static void __hyp_text __activate_traps_nvhe(struct kvm_vcpu *vcpu)
 
        write_sysreg(val, cptr_el2);
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                struct kvm_cpu_context *ctxt = &vcpu->arch.ctxt;
 
                isb();
@@ -181,7 +181,7 @@ static void deactivate_traps_vhe(void)
         * above before we can switch to the EL2/EL0 translation regime used by
         * the host.
         */
-       asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT_VHE));
+       asm(ALTERNATIVE("nop", "isb", ARM64_WORKAROUND_SPECULATIVE_AT));
 
        write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1);
        write_sysreg(vectors, vbar_el1);
@@ -192,7 +192,7 @@ static void __hyp_text __deactivate_traps_nvhe(void)
 {
        u64 mdcr_el2 = read_sysreg(mdcr_el2);
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                u64 val;
 
                /*
index 6d2df9fe0b5d2dcc2e1c9b98229079da666c0c50..ea5d22fbdacfea872b1f386eba5c3df36086e8bd 100644 (file)
@@ -107,7 +107,8 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
        write_sysreg(ctxt->sys_regs[MPIDR_EL1],         vmpidr_el2);
        write_sysreg(ctxt->sys_regs[CSSELR_EL1],        csselr_el1);
 
-       if (!cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE)) {
+       if (has_vhe() ||
+           !cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                write_sysreg_el1(ctxt->sys_regs[SCTLR_EL1],     SYS_SCTLR);
                write_sysreg_el1(ctxt->sys_regs[TCR_EL1],       SYS_TCR);
        } else  if (!ctxt->__hyp_running_vcpu) {
@@ -138,7 +139,8 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
        write_sysreg(ctxt->sys_regs[PAR_EL1],           par_el1);
        write_sysreg(ctxt->sys_regs[TPIDR_EL1],         tpidr_el1);
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE) &&
+       if (!has_vhe() &&
+           cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT) &&
            ctxt->__hyp_running_vcpu) {
                /*
                 * Must only be done for host registers, hence the context
index ceaddbe4279f9717b85195c5828b61fb67b38bfd..d063a576d511be9ed08f233a49bbf1e7d1468b91 100644 (file)
@@ -23,7 +23,7 @@ static void __hyp_text __tlb_switch_to_guest_vhe(struct kvm *kvm,
 
        local_irq_save(cxt->flags);
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_VHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                /*
                 * For CPUs that are affected by ARM errata 1165522 or 1530923,
                 * we cannot trust stage-1 to be in a correct state at that
@@ -63,7 +63,7 @@ static void __hyp_text __tlb_switch_to_guest_vhe(struct kvm *kvm,
 static void __hyp_text __tlb_switch_to_guest_nvhe(struct kvm *kvm,
                                                  struct tlb_inv_context *cxt)
 {
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                u64 val;
 
                /*
@@ -79,8 +79,9 @@ static void __hyp_text __tlb_switch_to_guest_nvhe(struct kvm *kvm,
                isb();
        }
 
+       /* __load_guest_stage2() includes an ISB for the workaround. */
        __load_guest_stage2(kvm);
-       isb();
+       asm(ALTERNATIVE("isb", "nop", ARM64_WORKAROUND_SPECULATIVE_AT));
 }
 
 static void __hyp_text __tlb_switch_to_guest(struct kvm *kvm,
@@ -103,7 +104,7 @@ static void __hyp_text __tlb_switch_to_host_vhe(struct kvm *kvm,
        write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
        isb();
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_VHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                /* Restore the registers to what they were */
                write_sysreg_el1(cxt->tcr, SYS_TCR);
                write_sysreg_el1(cxt->sctlr, SYS_SCTLR);
@@ -117,7 +118,7 @@ static void __hyp_text __tlb_switch_to_host_nvhe(struct kvm *kvm,
 {
        write_sysreg(0, vttbr_el2);
 
-       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT_NVHE)) {
+       if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) {
                /* Ensure write of the host VMID */
                isb();
                /* Restore the host's TCR_EL1 */
index 30b7ea680f66cb2e303eff980baaccf4fbe78821..70cd7bcca4332537f053582097800284d1346134 100644 (file)
@@ -46,14 +46,6 @@ static const struct kvm_regs default_regs_reset32 = {
                        PSR_AA32_I_BIT | PSR_AA32_F_BIT),
 };
 
-static bool cpu_has_32bit_el1(void)
-{
-       u64 pfr0;
-
-       pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
-       return !!(pfr0 & 0x20);
-}
-
 /**
  * kvm_arch_vm_ioctl_check_extension
  *
@@ -66,7 +58,7 @@ int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 
        switch (ext) {
        case KVM_CAP_ARM_EL1_32BIT:
-               r = cpu_has_32bit_el1();
+               r = cpus_have_const_cap(ARM64_HAS_32BIT_EL1);
                break;
        case KVM_CAP_GUEST_DEBUG_HW_BPS:
                r = get_num_brps();
@@ -288,7 +280,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
        switch (vcpu->arch.target) {
        default:
                if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) {
-                       if (!cpu_has_32bit_el1())
+                       if (!cpus_have_const_cap(ARM64_HAS_32BIT_EL1))
                                goto out;
                        cpu_reset = &default_regs_reset32;
                } else {
@@ -340,11 +332,50 @@ out:
        return ret;
 }
 
-void kvm_set_ipa_limit(void)
+u32 get_kvm_ipa_limit(void)
+{
+       return kvm_ipa_limit;
+}
+
+int kvm_set_ipa_limit(void)
 {
-       unsigned int ipa_max, pa_max, va_max, parange;
+       unsigned int ipa_max, pa_max, va_max, parange, tgran_2;
+       u64 mmfr0;
+
+       mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
+       parange = cpuid_feature_extract_unsigned_field(mmfr0,
+                               ID_AA64MMFR0_PARANGE_SHIFT);
+
+       /*
+        * Check with ARMv8.5-GTG that our PAGE_SIZE is supported at
+        * Stage-2. If not, things will stop very quickly.
+        */
+       switch (PAGE_SIZE) {
+       default:
+       case SZ_4K:
+               tgran_2 = ID_AA64MMFR0_TGRAN4_2_SHIFT;
+               break;
+       case SZ_16K:
+               tgran_2 = ID_AA64MMFR0_TGRAN16_2_SHIFT;
+               break;
+       case SZ_64K:
+               tgran_2 = ID_AA64MMFR0_TGRAN64_2_SHIFT;
+               break;
+       }
+
+       switch (cpuid_feature_extract_unsigned_field(mmfr0, tgran_2)) {
+       default:
+       case 1:
+               kvm_err("PAGE_SIZE not supported at Stage-2, giving up\n");
+               return -EINVAL;
+       case 0:
+               kvm_debug("PAGE_SIZE supported at Stage-2 (default)\n");
+               break;
+       case 2:
+               kvm_debug("PAGE_SIZE supported at Stage-2 (advertised)\n");
+               break;
+       }
 
-       parange = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1) & 0x7;
        pa_max = id_aa64mmfr0_parange_to_phys_shift(parange);
 
        /* Clamp the IPA limit to the PA size supported by the kernel */
@@ -378,6 +409,8 @@ void kvm_set_ipa_limit(void)
             "KVM IPA limit (%d bit) is smaller than default size\n", ipa_max);
        kvm_ipa_limit = ipa_max;
        kvm_info("IPA Size Limit: %dbits\n", kvm_ipa_limit);
+
+       return 0;
 }
 
 /*
@@ -390,7 +423,7 @@ void kvm_set_ipa_limit(void)
  */
 int kvm_arm_setup_stage2(struct kvm *kvm, unsigned long type)
 {
-       u64 vtcr = VTCR_EL2_FLAGS;
+       u64 vtcr = VTCR_EL2_FLAGS, mmfr0;
        u32 parange, phys_shift;
        u8 lvls;
 
@@ -406,7 +439,9 @@ int kvm_arm_setup_stage2(struct kvm *kvm, unsigned long type)
                phys_shift = KVM_PHYS_SHIFT;
        }
 
-       parange = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1) & 7;
+       mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);
+       parange = cpuid_feature_extract_unsigned_field(mmfr0,
+                               ID_AA64MMFR0_PARANGE_SHIFT);
        if (parange > ID_AA64MMFR0_PARANGE_MAX)
                parange = ID_AA64MMFR0_PARANGE_MAX;
        vtcr |= parange << VTCR_EL2_PS_SHIFT;
index 51db934702b6494523bced028c2df367ff1cdc03..7d7a39b01135c42cbbccbdab1dbd3a0146584b6f 100644 (file)
@@ -1456,9 +1456,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        ID_SANITISED(MVFR1_EL1),
        ID_SANITISED(MVFR2_EL1),
        ID_UNALLOCATED(3,3),
-       ID_UNALLOCATED(3,4),
-       ID_UNALLOCATED(3,5),
-       ID_UNALLOCATED(3,6),
+       ID_SANITISED(ID_PFR2_EL1),
+       ID_HIDDEN(ID_DFR1_EL1),
+       ID_SANITISED(ID_MMFR5_EL1),
        ID_UNALLOCATED(3,7),
 
        /* AArch64 ID registers */
index 8e25e89ad01fd7daa41065bae7e1c9745dce986b..0f8a3a9e3795b64b6cb0e53c0953e3b1833884e4 100644 (file)
  *     x0 - bytes not copied
  */
 
-       .macro ldrb1 ptr, regB, val
-       uao_user_alternative 9998f, ldrb, ldtrb, \ptr, \regB, \val
+       .macro ldrb1 reg, ptr, val
+       uao_user_alternative 9998f, ldrb, ldtrb, \reg, \ptr, \val
        .endm
 
-       .macro strb1 ptr, regB, val
-       strb \ptr, [\regB], \val
+       .macro strb1 reg, ptr, val
+       strb \reg, [\ptr], \val
        .endm
 
-       .macro ldrh1 ptr, regB, val
-       uao_user_alternative 9998f, ldrh, ldtrh, \ptr, \regB, \val
+       .macro ldrh1 reg, ptr, val
+       uao_user_alternative 9998f, ldrh, ldtrh, \reg, \ptr, \val
        .endm
 
-       .macro strh1 ptr, regB, val
-       strh \ptr, [\regB], \val
+       .macro strh1 reg, ptr, val
+       strh \reg, [\ptr], \val
        .endm
 
-       .macro ldr1 ptr, regB, val
-       uao_user_alternative 9998f, ldr, ldtr, \ptr, \regB, \val
+       .macro ldr1 reg, ptr, val
+       uao_user_alternative 9998f, ldr, ldtr, \reg, \ptr, \val
        .endm
 
-       .macro str1 ptr, regB, val
-       str \ptr, [\regB], \val
+       .macro str1 reg, ptr, val
+       str \reg, [\ptr], \val
        .endm
 
-       .macro ldp1 ptr, regB, regC, val
-       uao_ldp 9998f, \ptr, \regB, \regC, \val
+       .macro ldp1 reg1, reg2, ptr, val
+       uao_ldp 9998f, \reg1, \reg2, \ptr, \val
        .endm
 
-       .macro stp1 ptr, regB, regC, val
-       stp \ptr, \regB, [\regC], \val
+       .macro stp1 reg1, reg2, ptr, val
+       stp \reg1, \reg2, [\ptr], \val
        .endm
 
 end    .req    x5
index 667139013ed171ef4b5de1ba916941060858475c..80e37ada0ee1a501d5d4fb9fe87db9fc5853af2b 100644 (file)
  * Returns:
  *     x0 - bytes not copied
  */
-       .macro ldrb1 ptr, regB, val
-       uao_user_alternative 9998f, ldrb, ldtrb, \ptr, \regB, \val
+       .macro ldrb1 reg, ptr, val
+       uao_user_alternative 9998f, ldrb, ldtrb, \reg, \ptr, \val
        .endm
 
-       .macro strb1 ptr, regB, val
-       uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
+       .macro strb1 reg, ptr, val
+       uao_user_alternative 9998f, strb, sttrb, \reg, \ptr, \val
        .endm
 
-       .macro ldrh1 ptr, regB, val
-       uao_user_alternative 9998f, ldrh, ldtrh, \ptr, \regB, \val
+       .macro ldrh1 reg, ptr, val
+       uao_user_alternative 9998f, ldrh, ldtrh, \reg, \ptr, \val
        .endm
 
-       .macro strh1 ptr, regB, val
-       uao_user_alternative 9998f, strh, sttrh, \ptr, \regB, \val
+       .macro strh1 reg, ptr, val
+       uao_user_alternative 9998f, strh, sttrh, \reg, \ptr, \val
        .endm
 
-       .macro ldr1 ptr, regB, val
-       uao_user_alternative 9998f, ldr, ldtr, \ptr, \regB, \val
+       .macro ldr1 reg, ptr, val
+       uao_user_alternative 9998f, ldr, ldtr, \reg, \ptr, \val
        .endm
 
-       .macro str1 ptr, regB, val
-       uao_user_alternative 9998f, str, sttr, \ptr, \regB, \val
+       .macro str1 reg, ptr, val
+       uao_user_alternative 9998f, str, sttr, \reg, \ptr, \val
        .endm
 
-       .macro ldp1 ptr, regB, regC, val
-       uao_ldp 9998f, \ptr, \regB, \regC, \val
+       .macro ldp1 reg1, reg2, ptr, val
+       uao_ldp 9998f, \reg1, \reg2, \ptr, \val
        .endm
 
-       .macro stp1 ptr, regB, regC, val
-       uao_stp 9998f, \ptr, \regB, \regC, \val
+       .macro stp1 reg1, reg2, ptr, val
+       uao_stp 9998f, \reg1, \reg2, \ptr, \val
        .endm
 
 end    .req    x5
index 1a104d0089f3a4036574bb6d1c5ac9796740dc5b..4ec59704b8f2d19933ec1f39ca48e76315309609 100644 (file)
  * Returns:
  *     x0 - bytes not copied
  */
-       .macro ldrb1 ptr, regB, val
-       ldrb  \ptr, [\regB], \val
+       .macro ldrb1 reg, ptr, val
+       ldrb  \reg, [\ptr], \val
        .endm
 
-       .macro strb1 ptr, regB, val
-       uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
+       .macro strb1 reg, ptr, val
+       uao_user_alternative 9998f, strb, sttrb, \reg, \ptr, \val
        .endm
 
-       .macro ldrh1 ptr, regB, val
-       ldrh  \ptr, [\regB], \val
+       .macro ldrh1 reg, ptr, val
+       ldrh  \reg, [\ptr], \val
        .endm
 
-       .macro strh1 ptr, regB, val
-       uao_user_alternative 9998f, strh, sttrh, \ptr, \regB, \val
+       .macro strh1 reg, ptr, val
+       uao_user_alternative 9998f, strh, sttrh, \reg, \ptr, \val
        .endm
 
-       .macro ldr1 ptr, regB, val
-       ldr \ptr, [\regB], \val
+       .macro ldr1 reg, ptr, val
+       ldr \reg, [\ptr], \val
        .endm
 
-       .macro str1 ptr, regB, val
-       uao_user_alternative 9998f, str, sttr, \ptr, \regB, \val
+       .macro str1 reg, ptr, val
+       uao_user_alternative 9998f, str, sttr, \reg, \ptr, \val
        .endm
 
-       .macro ldp1 ptr, regB, regC, val
-       ldp \ptr, \regB, [\regC], \val
+       .macro ldp1 reg1, reg2, ptr, val
+       ldp \reg1, \reg2, [\ptr], \val
        .endm
 
-       .macro stp1 ptr, regB, regC, val
-       uao_stp 9998f, \ptr, \regB, \regC, \val
+       .macro stp1 reg1, reg2, ptr, val
+       uao_stp 9998f, \reg1, \reg2, \ptr, \val
        .endm
 
 end    .req    x5
index 243e107e98963b21552ea2cc2ee52e9fc24026e3..0f9e10ecda231ccccac18c54a57f79e4e4446603 100644 (file)
@@ -9,7 +9,7 @@
 #include <asm/alternative.h>
 #include <asm/assembler.h>
 
-       .cpu            generic+crc
+       .arch           armv8-a+crc
 
        .macro          __crc32, c
        cmp             x2, #16
index 9f382adfa88a221b481c1ec4bc94ade038e754af..e0bf83d556f23a48883562087d544a714dbe04ca 100644 (file)
  * Returns:
  *     x0 - dest
  */
-       .macro ldrb1 ptr, regB, val
-       ldrb  \ptr, [\regB], \val
+       .macro ldrb1 reg, ptr, val
+       ldrb  \reg, [\ptr], \val
        .endm
 
-       .macro strb1 ptr, regB, val
-       strb \ptr, [\regB], \val
+       .macro strb1 reg, ptr, val
+       strb \reg, [\ptr], \val
        .endm
 
-       .macro ldrh1 ptr, regB, val
-       ldrh  \ptr, [\regB], \val
+       .macro ldrh1 reg, ptr, val
+       ldrh  \reg, [\ptr], \val
        .endm
 
-       .macro strh1 ptr, regB, val
-       strh \ptr, [\regB], \val
+       .macro strh1 reg, ptr, val
+       strh \reg, [\ptr], \val
        .endm
 
-       .macro ldr1 ptr, regB, val
-       ldr \ptr, [\regB], \val
+       .macro ldr1 reg, ptr, val
+       ldr \reg, [\ptr], \val
        .endm
 
-       .macro str1 ptr, regB, val
-       str \ptr, [\regB], \val
+       .macro str1 reg, ptr, val
+       str \reg, [\ptr], \val
        .endm
 
-       .macro ldp1 ptr, regB, regC, val
-       ldp \ptr, \regB, [\regC], \val
+       .macro ldp1 reg1, reg2, ptr, val
+       ldp \reg1, \reg2, [\ptr], \val
        .endm
 
-       .macro stp1 ptr, regB, regC, val
-       stp \ptr, \regB, [\regC], \val
+       .macro stp1 reg1, reg2, ptr, val
+       stp \reg1, \reg2, [\ptr], \val
        .endm
 
        .weak memcpy
index 9b26f9a88724f11d3649152531c57b5b673b91e0..d702d60e64dab00f28a1918cbf714e6b7e1a81fc 100644 (file)
@@ -92,6 +92,9 @@ static void set_reserved_asid_bits(void)
                bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
 }
 
+#define asid_gen_match(asid) \
+       (!(((asid) ^ atomic64_read(&asid_generation)) >> asid_bits))
+
 static void flush_context(void)
 {
        int i;
@@ -220,8 +223,7 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
         *   because atomic RmWs are totally ordered for a given location.
         */
        old_active_asid = atomic64_read(&per_cpu(active_asids, cpu));
-       if (old_active_asid &&
-           !((asid ^ atomic64_read(&asid_generation)) >> asid_bits) &&
+       if (old_active_asid && asid_gen_match(asid) &&
            atomic64_cmpxchg_relaxed(&per_cpu(active_asids, cpu),
                                     old_active_asid, asid))
                goto switch_mm_fastpath;
@@ -229,7 +231,7 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
        raw_spin_lock_irqsave(&cpu_asid_lock, flags);
        /* Check that our ASID belongs to the current generation. */
        asid = atomic64_read(&mm->context.id);
-       if ((asid ^ atomic64_read(&asid_generation)) >> asid_bits) {
+       if (!asid_gen_match(asid)) {
                asid = new_context(mm);
                atomic64_set(&mm->context.id, asid);
        }
index 860c00ec8bd32a7307ef2326df25364f52c609c9..0da020c563e6b4e17bf6022dc4f6b9a3cf810723 100644 (file)
@@ -145,6 +145,11 @@ static const struct prot_bits pte_bits[] = {
                .val    = PTE_UXN,
                .set    = "UXN",
                .clear  = "   ",
+       }, {
+               .mask   = PTE_GP,
+               .val    = PTE_GP,
+               .set    = "GP",
+               .clear  = "  ",
        }, {
                .mask   = PTE_ATTRINDX_MASK,
                .val    = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
@@ -247,7 +252,7 @@ static void note_prot_wx(struct pg_state *st, unsigned long addr)
 }
 
 static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
-                     unsigned long val)
+                     u64 val)
 {
        struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
        static const char units[] = "KMGTPE";
index e42727e3568ea93afed405a7ec2cd4dbb1a41c93..d2df416b840ee3ddd10edcab059f1d8d8a1ef2e3 100644 (file)
@@ -272,7 +272,7 @@ int pfn_valid(unsigned long pfn)
        if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
                return 0;
 
-       if (!valid_section(__nr_to_section(pfn_to_section_nr(pfn))))
+       if (!valid_section(__pfn_to_section(pfn)))
                return 0;
 #endif
        return memblock_is_map_memory(addr);
index a374e4f51a6259340cff33a342138c517d169ec8..c299b73dd5e4e24f9e54fc7e18ae7db4346c8f3a 100644 (file)
@@ -609,6 +609,22 @@ static int __init map_entry_trampoline(void)
 core_initcall(map_entry_trampoline);
 #endif
 
+/*
+ * Open coded check for BTI, only for use to determine configuration
+ * for early mappings for before the cpufeature code has run.
+ */
+static bool arm64_early_this_cpu_has_bti(void)
+{
+       u64 pfr1;
+
+       if (!IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
+               return false;
+
+       pfr1 = read_sysreg_s(SYS_ID_AA64PFR1_EL1);
+       return cpuid_feature_extract_unsigned_field(pfr1,
+                                                   ID_AA64PFR1_BT_SHIFT);
+}
+
 /*
  * Create fine-grained mappings for the kernel.
  */
@@ -624,6 +640,14 @@ static void __init map_kernel(pgd_t *pgdp)
         */
        pgprot_t text_prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
 
+       /*
+        * If we have a CPU that supports BTI and a kernel built for
+        * BTI then mark the kernel executable text as guarded pages
+        * now so we don't have to rewrite the page tables later.
+        */
+       if (arm64_early_this_cpu_has_bti())
+               text_prot = __pgprot_modify(text_prot, PTE_GP, PTE_GP);
+
        /*
         * Only rodata will be remapped with different permissions later on,
         * all other segments are allowed to use contiguous mappings.
index 250c49008d73c1fefdd4aa9dfdf7797426d96fde..bde08090b8389d5fe3c438eb65e6b2fc9a27a436 100644 (file)
@@ -126,13 +126,13 @@ int set_memory_nx(unsigned long addr, int numpages)
 {
        return change_memory_common(addr, numpages,
                                        __pgprot(PTE_PXN),
-                                       __pgprot(0));
+                                       __pgprot(PTE_MAYBE_GP));
 }
 
 int set_memory_x(unsigned long addr, int numpages)
 {
        return change_memory_common(addr, numpages,
-                                       __pgprot(0),
+                                       __pgprot(PTE_MAYBE_GP),
                                        __pgprot(PTE_PXN));
 }
 
index 197a9ba2d5ea2c859bbc2ce2445f4ee55c09a3df..b7bebb12a56d78e39f88558e18c2122fd2127ccf 100644 (file)
@@ -58,6 +58,8 @@
  * cpu_do_suspend - save CPU registers context
  *
  * x0: virtual address of context pointer
+ *
+ * This must be kept in sync with struct cpu_suspend_ctx in <asm/suspend.h>.
  */
 SYM_FUNC_START(cpu_do_suspend)
        mrs     x2, tpidr_el0
@@ -82,6 +84,11 @@ alternative_endif
        stp     x8, x9, [x0, #48]
        stp     x10, x11, [x0, #64]
        stp     x12, x13, [x0, #80]
+       /*
+        * Save x18 as it may be used as a platform register, e.g. by shadow
+        * call stack.
+        */
+       str     x18, [x0, #96]
        ret
 SYM_FUNC_END(cpu_do_suspend)
 
@@ -98,6 +105,13 @@ SYM_FUNC_START(cpu_do_resume)
        ldp     x9, x10, [x0, #48]
        ldp     x11, x12, [x0, #64]
        ldp     x13, x14, [x0, #80]
+       /*
+        * Restore x18, as it may be used as a platform register, and clear
+        * the buffer to minimize the risk of exposure when used for shadow
+        * call stack.
+        */
+       ldr     x18, [x0, #96]
+       str     xzr, [x0, #96]
        msr     tpidr_el0, x2
        msr     tpidrro_el0, x3
        msr     contextidr_el1, x4
@@ -139,7 +153,7 @@ alternative_if ARM64_HAS_RAS_EXTN
        msr_s   SYS_DISR_EL1, xzr
 alternative_else_nop_endif
 
-       ptrauth_keys_install_kernel x14, 0, x1, x2, x3
+       ptrauth_keys_install_kernel_nosync x14, x1, x2, x3
        isb
        ret
 SYM_FUNC_END(cpu_do_resume)
@@ -386,8 +400,6 @@ SYM_FUNC_END(idmap_kpti_install_ng_mappings)
  *
  *     Initialise the processor for turning the MMU on.
  *
- * Input:
- *     x0 with a flag ARM64_CPU_BOOT_PRIMARY/ARM64_CPU_BOOT_SECONDARY/ARM64_CPU_RUNTIME.
  * Output:
  *     Return in x0 the value of the SCTLR_EL1 register.
  */
@@ -446,51 +458,9 @@ SYM_FUNC_START(__cpu_setup)
 1:
 #endif /* CONFIG_ARM64_HW_AFDBM */
        msr     tcr_el1, x10
-       mov     x1, x0
        /*
         * Prepare SCTLR
         */
        mov_q   x0, SCTLR_EL1_SET
-
-#ifdef CONFIG_ARM64_PTR_AUTH
-       /* No ptrauth setup for run time cpus */
-       cmp     x1, #ARM64_CPU_RUNTIME
-       b.eq    3f
-
-       /* Check if the CPU supports ptrauth */
-       mrs     x2, id_aa64isar1_el1
-       ubfx    x2, x2, #ID_AA64ISAR1_APA_SHIFT, #8
-       cbz     x2, 3f
-
-       /*
-        * The primary cpu keys are reset here and can be
-        * re-initialised with some proper values later.
-        */
-       msr_s   SYS_APIAKEYLO_EL1, xzr
-       msr_s   SYS_APIAKEYHI_EL1, xzr
-
-       /* Just enable ptrauth for primary cpu */
-       cmp     x1, #ARM64_CPU_BOOT_PRIMARY
-       b.eq    2f
-
-       /* if !system_supports_address_auth() then skip enable */
-alternative_if_not ARM64_HAS_ADDRESS_AUTH
-       b       3f
-alternative_else_nop_endif
-
-       /* Install ptrauth key for secondary cpus */
-       adr_l   x2, secondary_data
-       ldr     x3, [x2, #CPU_BOOT_TASK]        // get secondary_data.task
-       cbz     x3, 2f                          // check for slow booting cpus
-       ldp     x3, x4, [x2, #CPU_BOOT_PTRAUTH_KEY]
-       msr_s   SYS_APIAKEYLO_EL1, x3
-       msr_s   SYS_APIAKEYHI_EL1, x4
-
-2:     /* Enable ptrauth instructions */
-       ldr     x2, =SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | \
-                    SCTLR_ELx_ENDA | SCTLR_ELx_ENDB
-       orr     x0, x0, x2
-3:
-#endif
        ret                                     // return to head.S
 SYM_FUNC_END(__cpu_setup)
index eb73f9f72c467a1bbacecb7ea270b75066b8d2ab..cc0cf0f5c7c3b8d1a15d6296462282b5e8ccc8c2 100644 (file)
 /* Rd = Rn OP imm12 */
 #define A64_ADD_I(sf, Rd, Rn, imm12) A64_ADDSUB_IMM(sf, Rd, Rn, imm12, ADD)
 #define A64_SUB_I(sf, Rd, Rn, imm12) A64_ADDSUB_IMM(sf, Rd, Rn, imm12, SUB)
+#define A64_ADDS_I(sf, Rd, Rn, imm12) \
+       A64_ADDSUB_IMM(sf, Rd, Rn, imm12, ADD_SETFLAGS)
+#define A64_SUBS_I(sf, Rd, Rn, imm12) \
+       A64_ADDSUB_IMM(sf, Rd, Rn, imm12, SUB_SETFLAGS)
+/* Rn + imm12; set condition flags */
+#define A64_CMN_I(sf, Rn, imm12) A64_ADDS_I(sf, A64_ZR, Rn, imm12)
+/* Rn - imm12; set condition flags */
+#define A64_CMP_I(sf, Rn, imm12) A64_SUBS_I(sf, A64_ZR, Rn, imm12)
 /* Rd = Rn */
 #define A64_MOV(sf, Rd, Rn) A64_ADD_I(sf, Rd, Rn, 0)
 
 /* Rn & Rm; set condition flags */
 #define A64_TST(sf, Rn, Rm) A64_ANDS(sf, A64_ZR, Rn, Rm)
 
+/* Logical (immediate) */
+#define A64_LOGIC_IMM(sf, Rd, Rn, imm, type) ({ \
+       u64 imm64 = (sf) ? (u64)imm : (u64)(u32)imm; \
+       aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_##type, \
+               A64_VARIANT(sf), Rn, Rd, imm64); \
+})
+/* Rd = Rn OP imm */
+#define A64_AND_I(sf, Rd, Rn, imm) A64_LOGIC_IMM(sf, Rd, Rn, imm, AND)
+#define A64_ORR_I(sf, Rd, Rn, imm) A64_LOGIC_IMM(sf, Rd, Rn, imm, ORR)
+#define A64_EOR_I(sf, Rd, Rn, imm) A64_LOGIC_IMM(sf, Rd, Rn, imm, EOR)
+#define A64_ANDS_I(sf, Rd, Rn, imm) A64_LOGIC_IMM(sf, Rd, Rn, imm, AND_SETFLAGS)
+/* Rn & imm; set condition flags */
+#define A64_TST_I(sf, Rn, imm) A64_ANDS_I(sf, A64_ZR, Rn, imm)
+
+/* HINTs */
+#define A64_HINT(x) aarch64_insn_gen_hint(x)
+
+/* BTI */
+#define A64_BTI_C  A64_HINT(AARCH64_INSN_HINT_BTIC)
+#define A64_BTI_J  A64_HINT(AARCH64_INSN_HINT_BTIJ)
+#define A64_BTI_JC A64_HINT(AARCH64_INSN_HINT_BTIJC)
+
 #endif /* _BPF_JIT_H */
index cdc79de0c794af4e46176c6924b14fd127426f54..3cb25b43b368e3c231ddd508ae064d6ed99065fb 100644 (file)
@@ -167,11 +167,21 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
        return to - from;
 }
 
+static bool is_addsub_imm(u32 imm)
+{
+       /* Either imm12 or shifted imm12. */
+       return !(imm & ~0xfff) || !(imm & ~0xfff000);
+}
+
 /* Stack must be multiples of 16B */
 #define STACK_ALIGN(sz) (((sz) + 15) & ~15)
 
 /* Tail call offset to jump into */
+#if IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)
+#define PROLOGUE_OFFSET 8
+#else
 #define PROLOGUE_OFFSET 7
+#endif
 
 static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
 {
@@ -208,6 +218,10 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
         *
         */
 
+       /* BTI landing pad */
+       if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
+               emit(A64_BTI_C, ctx);
+
        /* Save FP and LR registers to stay align with ARM64 AAPCS */
        emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
        emit(A64_MOV(1, A64_FP, A64_SP), ctx);
@@ -230,6 +244,10 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
                                    cur_offset, PROLOGUE_OFFSET);
                        return -1;
                }
+
+               /* BTI landing pad for the tail call, done with a BR */
+               if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
+                       emit(A64_BTI_J, ctx);
        }
 
        ctx->stack_size = STACK_ALIGN(prog->aux->stack_depth);
@@ -356,6 +374,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
        const bool isdw = BPF_SIZE(code) == BPF_DW;
        u8 jmp_cond, reg;
        s32 jmp_offset;
+       u32 a64_insn;
 
 #define check_imm(bits, imm) do {                              \
        if ((((imm) > 0) && ((imm) >> (bits))) ||               \
@@ -478,28 +497,55 @@ emit_bswap_uxt:
        /* dst = dst OP imm */
        case BPF_ALU | BPF_ADD | BPF_K:
        case BPF_ALU64 | BPF_ADD | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_ADD(is64, dst, dst, tmp), ctx);
+               if (is_addsub_imm(imm)) {
+                       emit(A64_ADD_I(is64, dst, dst, imm), ctx);
+               } else if (is_addsub_imm(-imm)) {
+                       emit(A64_SUB_I(is64, dst, dst, -imm), ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_ADD(is64, dst, dst, tmp), ctx);
+               }
                break;
        case BPF_ALU | BPF_SUB | BPF_K:
        case BPF_ALU64 | BPF_SUB | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_SUB(is64, dst, dst, tmp), ctx);
+               if (is_addsub_imm(imm)) {
+                       emit(A64_SUB_I(is64, dst, dst, imm), ctx);
+               } else if (is_addsub_imm(-imm)) {
+                       emit(A64_ADD_I(is64, dst, dst, -imm), ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_SUB(is64, dst, dst, tmp), ctx);
+               }
                break;
        case BPF_ALU | BPF_AND | BPF_K:
        case BPF_ALU64 | BPF_AND | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_AND(is64, dst, dst, tmp), ctx);
+               a64_insn = A64_AND_I(is64, dst, dst, imm);
+               if (a64_insn != AARCH64_BREAK_FAULT) {
+                       emit(a64_insn, ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_AND(is64, dst, dst, tmp), ctx);
+               }
                break;
        case BPF_ALU | BPF_OR | BPF_K:
        case BPF_ALU64 | BPF_OR | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_ORR(is64, dst, dst, tmp), ctx);
+               a64_insn = A64_ORR_I(is64, dst, dst, imm);
+               if (a64_insn != AARCH64_BREAK_FAULT) {
+                       emit(a64_insn, ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_ORR(is64, dst, dst, tmp), ctx);
+               }
                break;
        case BPF_ALU | BPF_XOR | BPF_K:
        case BPF_ALU64 | BPF_XOR | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_EOR(is64, dst, dst, tmp), ctx);
+               a64_insn = A64_EOR_I(is64, dst, dst, imm);
+               if (a64_insn != AARCH64_BREAK_FAULT) {
+                       emit(a64_insn, ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_EOR(is64, dst, dst, tmp), ctx);
+               }
                break;
        case BPF_ALU | BPF_MUL | BPF_K:
        case BPF_ALU64 | BPF_MUL | BPF_K:
@@ -623,13 +669,24 @@ emit_cond_jmp:
        case BPF_JMP32 | BPF_JSLT | BPF_K:
        case BPF_JMP32 | BPF_JSGE | BPF_K:
        case BPF_JMP32 | BPF_JSLE | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_CMP(is64, dst, tmp), ctx);
+               if (is_addsub_imm(imm)) {
+                       emit(A64_CMP_I(is64, dst, imm), ctx);
+               } else if (is_addsub_imm(-imm)) {
+                       emit(A64_CMN_I(is64, dst, -imm), ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_CMP(is64, dst, tmp), ctx);
+               }
                goto emit_cond_jmp;
        case BPF_JMP | BPF_JSET | BPF_K:
        case BPF_JMP32 | BPF_JSET | BPF_K:
-               emit_a64_mov_i(is64, tmp, imm, ctx);
-               emit(A64_TST(is64, dst, tmp), ctx);
+               a64_insn = A64_TST_I(is64, dst, imm);
+               if (a64_insn != AARCH64_BREAK_FAULT) {
+                       emit(a64_insn, ctx);
+               } else {
+                       emit_a64_mov_i(is64, tmp, imm, ctx);
+                       emit(A64_TST(is64, dst, tmp), ctx);
+               }
                goto emit_cond_jmp;
        /* function call */
        case BPF_JMP | BPF_CALL:
index 46940844c553feff86bf18ca8115ba89fe6afe8e..335ca4900808478517178939bef0f7607cb1dbf1 100644 (file)
@@ -4,28 +4,6 @@
 #include <linux/module.h>
 #include <net/checksum.h>
 
-#include <asm/byteorder.h>
-
-/*
- * copy from fs while checksumming, otherwise like csum_partial
- */
-__wsum
-csum_partial_copy_from_user(const void __user *src, void *dst, int len,
-                           __wsum sum, int *csum_err)
-{
-       int missing;
-
-       missing = __copy_from_user(dst, src, len);
-       if (missing) {
-               memset(dst + len - missing, 0, missing);
-               *csum_err = -EFAULT;
-       } else
-               *csum_err = 0;
-
-       return csum_partial(dst, len, sum);
-}
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-
 /* These are from csum_64plus.S */
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(csum_partial_copy);
index 61d94ec7dd160f119426a876152be597c65f4f7c..13c23e2c707ccfae861fedddfb16374a527a9776 100644 (file)
@@ -80,7 +80,6 @@
 .endm
 
 .macro RESTORE_ALL
-       psrclr  ie
        ldw     lr, (sp, 4)
        ldw     a0, (sp, 8)
        mtcr    a0, epc
        movi    r6, 0
        cpwcr   r6, cpcr31
 .endm
-
-.macro ANDI_R3 rx, imm
-       lsri    \rx, 3
-       andi    \rx, (\imm >> 3)
-.endm
 #endif /* __ASM_CSKY_ENTRY_H */
index ab63c41abccadf7c3ba2f2d6b0c526a5b5dad7ec..4fdd6c12e7ff8571cf82ca66061d4d3a4d39b991 100644 (file)
@@ -13,6 +13,8 @@
 #define LSAVE_A1       28
 #define LSAVE_A2       32
 #define LSAVE_A3       36
+#define LSAVE_A4       40
+#define LSAVE_A5       44
 
 #define KSPTOUSP
 #define USPTOKSP
@@ -63,7 +65,6 @@
 .endm
 
 .macro RESTORE_ALL
-       psrclr  ie
        ldw     tls, (sp, 0)
        ldw     lr, (sp, 4)
        ldw     a0, (sp, 8)
        jmpi    3f /* jump to va */
 3:
 .endm
-
-.macro ANDI_R3 rx, imm
-       lsri    \rx, 3
-       andi    \rx, (\imm >> 3)
-.endm
 #endif /* __ASM_CSKY_ENTRY_H */
index 5c61e84e790f888f6113905ab253612c66a03516..8980e4e643919d47861e2a381447f144c0f665d8 100644 (file)
@@ -81,4 +81,10 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_RESTORE_SIGMASK   (1 << TIF_RESTORE_SIGMASK)
 #define _TIF_SECCOMP           (1 << TIF_SECCOMP)
 
+#define _TIF_WORK_MASK         (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
+                                _TIF_NOTIFY_RESUME | _TIF_UPROBE)
+
+#define _TIF_SYSCALL_WORK      (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
+                                _TIF_SYSCALL_TRACEPOINT)
+
 #endif /* _ASM_CSKY_THREAD_INFO_H */
index 3760397fdd3d9ea83a563f42635a80fccdaeb002..f13800383a19aab64d09750dc5ec01cc1bea02d4 100644 (file)
@@ -128,39 +128,41 @@ tlbop_end 1
 ENTRY(csky_systemcall)
        SAVE_ALL TRAP0_SIZE
        zero_fp
-#ifdef CONFIG_RSEQ_DEBUG
-       mov     a0, sp
-       jbsr    rseq_syscall
-#endif
        psrset  ee, ie
 
-       lrw     r11, __NR_syscalls
-       cmphs   syscallid, r11          /* Check nr of syscall */
-       bt      ret_from_exception
+       lrw     r9, __NR_syscalls
+       cmphs   syscallid, r          /* Check nr of syscall */
+       bt      1f
 
-       lrw     r13, sys_call_table
-       ixw     r13, syscallid
-       ldw     r11, (r13)
-       cmpnei  r11, 0
+       lrw     r9, sys_call_table
+       ixw     r9, syscallid
+       ldw     syscallid, (r9)
+       cmpnei  syscallid, 0
        bf      ret_from_exception
 
        mov     r9, sp
        bmaski  r10, THREAD_SHIFT
        andn    r9, r10
-       ldw     r12, (r9, TINFO_FLAGS)
-       ANDI_R3 r12, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT)
-       cmpnei  r12, 0
+       ldw     r10, (r9, TINFO_FLAGS)
+       lrw     r9, _TIF_SYSCALL_WORK
+       and     r10, r9
+       cmpnei  r10, 0
        bt      csky_syscall_trace
 #if defined(__CSKYABIV2__)
        subi    sp, 8
        stw     r5, (sp, 0x4)
        stw     r4, (sp, 0x0)
-       jsr     r11                      /* Do system call */
+       jsr     syscallid                      /* Do system call */
        addi    sp, 8
 #else
-       jsr     r11
+       jsr     syscallid
 #endif
        stw     a0, (sp, LSAVE_A0)      /* Save return value */
+1:
+#ifdef CONFIG_DEBUG_RSEQ
+       mov     a0, sp
+       jbsr    rseq_syscall
+#endif
        jmpi    ret_from_exception
 
 csky_syscall_trace:
@@ -173,18 +175,23 @@ csky_syscall_trace:
        ldw     a3, (sp, LSAVE_A3)
 #if defined(__CSKYABIV2__)
        subi    sp, 8
-       stw     r5, (sp, 0x4)
-       stw     r4, (sp, 0x0)
+       ldw     r9, (sp, LSAVE_A4)
+       stw     r9, (sp, 0x0)
+       ldw     r9, (sp, LSAVE_A5)
+       stw     r9, (sp, 0x4)
+       jsr     syscallid                     /* Do system call */
+       addi    sp, 8
 #else
        ldw     r6, (sp, LSAVE_A4)
        ldw     r7, (sp, LSAVE_A5)
-#endif
-       jsr     r11                     /* Do system call */
-#if defined(__CSKYABIV2__)
-       addi    sp, 8
+       jsr     syscallid                     /* Do system call */
 #endif
        stw     a0, (sp, LSAVE_A0)      /* Save return value */
 
+#ifdef CONFIG_DEBUG_RSEQ
+       mov     a0, sp
+       jbsr    rseq_syscall
+#endif
        mov     a0, sp                  /* right now, sp --> pt_regs */
        jbsr    syscall_trace_exit
        br      ret_from_exception
@@ -200,18 +207,20 @@ ENTRY(ret_from_fork)
        mov     r9, sp
        bmaski  r10, THREAD_SHIFT
        andn    r9, r10
-       ldw     r12, (r9, TINFO_FLAGS)
-       ANDI_R3 r12, (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT)
-       cmpnei  r12, 0
+       ldw     r10, (r9, TINFO_FLAGS)
+       lrw     r9, _TIF_SYSCALL_WORK
+       and     r10, r9
+       cmpnei  r10, 0
        bf      ret_from_exception
        mov     a0, sp                  /* sp = pt_regs pointer */
        jbsr    syscall_trace_exit
 
 ret_from_exception:
-       ld      syscallid, (sp, LSAVE_PSR)
-       btsti   syscallid, 31
-       bt      1f
+       psrclr  ie
+       ld      r9, (sp, LSAVE_PSR)
+       btsti   r9, 31
 
+       bt      1f
        /*
         * Load address of current->thread_info, Then get address of task_struct
         * Get task_needreshed in task_struct
@@ -220,11 +229,24 @@ ret_from_exception:
        bmaski  r10, THREAD_SHIFT
        andn    r9, r10
 
-       ldw     r12, (r9, TINFO_FLAGS)
-       andi    r12, (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | _TIF_UPROBE)
-       cmpnei  r12, 0
+       ldw     r10, (r9, TINFO_FLAGS)
+       lrw     r9, _TIF_WORK_MASK
+       and     r10, r9
+       cmpnei  r10, 0
        bt      exit_work
 1:
+#ifdef CONFIG_PREEMPTION
+       mov     r9, sp
+       bmaski  r10, THREAD_SHIFT
+       andn    r9, r10
+
+       ldw     r10, (r9, TINFO_PREEMPT)
+       cmpnei  r10, 0
+       bt      2f
+       jbsr    preempt_schedule_irq    /* irq en/disable is done inside */
+2:
+#endif
+
 #ifdef CONFIG_TRACE_IRQFLAGS
        ld      r10, (sp, LSAVE_PSR)
        btsti   r10, 6
@@ -235,14 +257,15 @@ ret_from_exception:
        RESTORE_ALL
 
 exit_work:
-       lrw     syscallid, ret_from_exception
-       mov     lr, syscallid
+       lrw     r9, ret_from_exception
+       mov     lr, r9
 
-       btsti   r12, TIF_NEED_RESCHED
+       btsti   r10, TIF_NEED_RESCHED
        bt      work_resched
 
+       psrset  ie
        mov     a0, sp
-       mov     a1, r12
+       mov     a1, r10
        jmpi    do_notify_resume
 
 work_resched:
@@ -291,34 +314,10 @@ ENTRY(csky_irq)
        jbsr    trace_hardirqs_off
 #endif
 
-#ifdef CONFIG_PREEMPTION
-       mov     r9, sp                  /* Get current stack  pointer */
-       bmaski  r10, THREAD_SHIFT
-       andn    r9, r10                 /* Get thread_info */
-
-       /*
-        * Get task_struct->stack.preempt_count for current,
-        * and increase 1.
-        */
-       ldw     r12, (r9, TINFO_PREEMPT)
-       addi    r12, 1
-       stw     r12, (r9, TINFO_PREEMPT)
-#endif
 
        mov     a0, sp
        jbsr    csky_do_IRQ
 
-#ifdef CONFIG_PREEMPTION
-       subi    r12, 1
-       stw     r12, (r9, TINFO_PREEMPT)
-       cmpnei  r12, 0
-       bt      2f
-       ldw     r12, (r9, TINFO_FLAGS)
-       btsti   r12, TIF_NEED_RESCHED
-       bf      2f
-       jbsr    preempt_schedule_irq    /* irq en/disable is done inside */
-#endif
-2:
        jmpi    ret_from_exception
 
 /*
index 0ed18bc3f6cf2f416fe44a1baeee6e1d39b9b94a..2a1c64629cdcd91201e215d687cc75733e8d313a 100644 (file)
@@ -37,16 +37,6 @@ extern __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
  */
 extern __wsum csum_partial(const void *buff, int len, __wsum sum);
 
-/*
- * Same as csum_partial, but copies from src while it checksums.
- *
- * Here it is even more important to align src and dst on a 32-bit (or
- * even better 64-bit) boundary.
- */
-extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
-                                                int len, __wsum sum,
-                                                int *errp);
-
 extern __wsum csum_partial_copy_nocheck(const void *src, void *dst,
                                               int len, __wsum sum);
 
index 410a769ece9581f96bec0382567b313a17169401..3eb397415381077d2e5d01954f6945f4cc48b3d2 100644 (file)
@@ -6,7 +6,7 @@
 #define _ASM_IA64_DEVICE_H
 
 struct dev_archdata {
-#ifdef CONFIG_INTEL_IOMMU
+#ifdef CONFIG_IOMMU_API
        void *iommu; /* hook for IOMMU specific extension */
 #endif
 };
index f69f3fe0532e0ff4ae20e2466d3426961910b057..a54eacbc61a96ced57389ed510aa357bd53f6ce5 100644 (file)
@@ -57,12 +57,12 @@ unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR;
 unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR;
 
 static const efi_config_table_type_t arch_tables[] __initconst = {
-       {ESI_TABLE_GUID, "ESI", &esi_phys},
-       {HCDP_TABLE_GUID, "HCDP", &hcdp_phys},
-       {MPS_TABLE_GUID, "MPS", &mps_phys},
-       {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys},
-       {SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys},
-       {NULL_GUID, NULL, 0},
+       {ESI_TABLE_GUID,                                &esi_phys,              "ESI"           },
+       {HCDP_TABLE_GUID,                               &hcdp_phys,             "HCDP"          },
+       {MPS_TABLE_GUID,                                &mps_phys,              "MPS"           },
+       {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID,    &palo_phys,             "PALO"          },
+       {SAL_SYSTEM_TABLE_GUID,                         &sal_systab_phys,       "SALsystab"     },
+       {},
 };
 
 extern efi_status_t efi_call_phys (void *, ...);
index 042911e670b80179a74e0b55f8f8a644ccb49f1c..49e325b604b31917c1e3c967a2c1bae71c81ea7a 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index bf9396b1ed329139769a03d91d5d3ba5f8f14cf6..5d147a33d64899bddd5fdce24a44f167d0849a7b 100644 (file)
@@ -103,39 +103,11 @@ out:
  * This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS.
  * But it's very tricky to get right even in C.
  */
-extern unsigned long do_csum(const unsigned char *, long);
-
-__wsum
-csum_partial_copy_from_user(const void __user *src, void *dst,
-                               int len, __wsum psum, int *errp)
-{
-       unsigned long result;
-
-       /* XXX Fixme
-        * for now we separate the copy from checksum for obvious
-        * alignment difficulties. Look at the Alpha code and you'll be
-        * scared.
-        */
-
-       if (__copy_from_user(dst, src, len) != 0 && errp)
-               *errp = -EFAULT;
-
-       result = do_csum(dst, len);
-
-       /* add in old sum, and carry.. */
-       result += (__force u32)psum;
-       /* 32+c bits -> 32 bits */
-       result = (result & 0xffffffff) + (result >> 32);
-       return (__force __wsum)result;
-}
-
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-
 __wsum
 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 {
-       return csum_partial_copy_from_user((__force const void __user *)src,
-                                          dst, len, sum, NULL);
+       memcpy(dst, src, len);
+       return csum_partial(dst, len, sum);
 }
 
 EXPORT_SYMBOL(csum_partial_copy_nocheck);
index c32ab8041cf6b8dca70652ded719cae362c6c773..4eb911d64e8daf29610bc021f0c0ba2b86ac833e 100644 (file)
@@ -221,6 +221,7 @@ static void __init amiga_identify(void)
        case AMI_1200:
                AMIGAHW_SET(A1200_IDE);
                AMIGAHW_SET(PCMCIA);
+               fallthrough;
        case AMI_500:
        case AMI_500PLUS:
        case AMI_1000:
@@ -233,7 +234,7 @@ static void __init amiga_identify(void)
        case AMI_3000T:
                AMIGAHW_SET(AMBER_FF);
                AMIGAHW_SET(MAGIC_REKICK);
-               /* fall through */
+               fallthrough;
        case AMI_3000PLUS:
                AMIGAHW_SET(A3000_SCSI);
                AMIGAHW_SET(A3000_CLK);
@@ -242,7 +243,7 @@ static void __init amiga_identify(void)
 
        case AMI_4000T:
                AMIGAHW_SET(A4000_SCSI);
-               /* fall through */
+               fallthrough;
        case AMI_4000:
                AMIGAHW_SET(A4000_IDE);
                AMIGAHW_SET(A3000_CLK);
@@ -628,7 +629,7 @@ struct savekmsg {
        unsigned long magic2;           /* SAVEKMSG_MAGIC2 */
        unsigned long magicptr;         /* address of magic1 */
        unsigned long size;
-       char data[0];
+       char data[];
 };
 
 static struct savekmsg *savekmsg;
index b4103b6bfdeb681e14b91cdea3b9c8e6b0b0c1f2..9ef4ec0aea00861c13be4c4d1b88c5725ea2bd12 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/mcfqspi.h>
 #include <linux/platform_data/edma.h>
 #include <linux/platform_data/dma-mcf-edma.h>
+#include <linux/platform_data/mmc-esdhc-mcf.h>
 
 /*
  *     All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
@@ -551,9 +552,35 @@ static struct platform_device mcf_edma = {
                .platform_data = &mcf_edma_data,
        }
 };
-
 #endif /* IS_ENABLED(CONFIG_MCF_EDMA) */
 
+#if IS_ENABLED(CONFIG_MMC)
+static struct mcf_esdhc_platform_data mcf_esdhc_data = {
+       .max_bus_width = 4,
+       .cd_type = ESDHC_CD_NONE,
+};
+
+static struct resource mcf_esdhc_resources[] = {
+       {
+               .start = MCFSDHC_BASE,
+               .end = MCFSDHC_BASE + MCFSDHC_SIZE - 1,
+               .flags = IORESOURCE_MEM,
+       }, {
+               .start = MCF_IRQ_SDHC,
+               .end = MCF_IRQ_SDHC,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device mcf_esdhc = {
+       .name                   = "sdhci-esdhc-mcf",
+       .id                     = 0,
+       .num_resources          = ARRAY_SIZE(mcf_esdhc_resources),
+       .resource               = mcf_esdhc_resources,
+       .dev.platform_data      = &mcf_esdhc_data,
+};
+#endif /* IS_ENABLED(CONFIG_MMC) */
+
 static struct platform_device *mcf_devices[] __initdata = {
        &mcf_uart,
 #if IS_ENABLED(CONFIG_FEC)
@@ -586,6 +613,9 @@ static struct platform_device *mcf_devices[] __initdata = {
 #if IS_ENABLED(CONFIG_MCF_EDMA)
        &mcf_edma,
 #endif
+#if IS_ENABLED(CONFIG_MMC)
+       &mcf_esdhc,
+#endif
 };
 
 /*
@@ -614,4 +644,3 @@ static int __init mcf_init_devices(void)
 }
 
 arch_initcall(mcf_init_devices);
-
index 5bd24c9b865dc2ec85a2913bbc16ba70324ee5b3..1e5259a652d1e4e16d1775cdba327dfd4b184107 100644 (file)
@@ -52,7 +52,7 @@ DEFINE_CLK(0, "mcfssi.0", 47, MCF_CLK);
 DEFINE_CLK(0, "pll.0", 48, MCF_CLK);
 DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
 DEFINE_CLK(0, "mcfssi.1", 50, MCF_CLK);
-DEFINE_CLK(0, "mcfsdhc.0", 51, MCF_CLK);
+DEFINE_CLK(0, "sdhci-esdhc-mcf.0", 51, MCF_CLK);
 DEFINE_CLK(0, "enet-fec.0", 53, MCF_CLK);
 DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
 DEFINE_CLK(0, "switch.0", 55, MCF_CLK);
@@ -74,6 +74,10 @@ DEFINE_CLK(1, "mcfpwm.0", 34, MCF_BUSCLK);
 DEFINE_CLK(1, "sys.0", 36, MCF_BUSCLK);
 DEFINE_CLK(1, "gpio.0", 37, MCF_BUSCLK);
 
+DEFINE_CLK(2, "ipg.0", 0, MCF_CLK);
+DEFINE_CLK(2, "ahb.0", 1, MCF_CLK);
+DEFINE_CLK(2, "per.0", 2, MCF_CLK);
+
 struct clk *mcf_clks[] = {
        &__clk_0_2,
        &__clk_0_8,
@@ -131,6 +135,11 @@ struct clk *mcf_clks[] = {
        &__clk_1_34,
        &__clk_1_36,
        &__clk_1_37,
+
+       &__clk_2_0,
+       &__clk_2_1,
+       &__clk_2_2,
+
        NULL,
 };
 
@@ -151,6 +160,7 @@ static struct clk * const enable_clks[] __initconst = {
        &__clk_0_33, /* pit.1 */
        &__clk_0_37, /* eport */
        &__clk_0_48, /* pll */
+       &__clk_0_51, /* esdhc */
 
        &__clk_1_36, /* CCM/reset module/Power management */
        &__clk_1_37, /* gpio */
@@ -194,6 +204,21 @@ static struct clk * const disable_clks[] __initconst = {
        &__clk_1_29, /* uart 9 */
 };
 
+static void __clk_enable2(struct clk *clk)
+{
+       __raw_writel(__raw_readl(MCFSDHC_CLK) | (1 << clk->slot), MCFSDHC_CLK);
+}
+
+static void __clk_disable2(struct clk *clk)
+{
+       __raw_writel(__raw_readl(MCFSDHC_CLK) & ~(1 << clk->slot), MCFSDHC_CLK);
+}
+
+struct clk_ops clk_ops2 = {
+       .enable         = __clk_enable2,
+       .disable        = __clk_disable2,
+};
+
 static void __init m5441x_clk_init(void)
 {
        unsigned i;
index 5b3a273ae3da388e730a5cf533e6b5e1e739a917..888b75e7fd79071f46126c8eceeff1403f6049cd 100644 (file)
@@ -100,7 +100,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -381,6 +380,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -452,6 +452,7 @@ CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_MSM6242=m
 CONFIG_RTC_DRV_RP5C01=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -472,6 +473,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -619,9 +621,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 0bf0907a7c801cbe8037cd9e8010bc5abdaa6286..45303846b65950e3b3750ddae0173582b8c46d53 100644 (file)
@@ -96,7 +96,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -360,6 +359,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -408,6 +408,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -428,6 +429,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -575,9 +577,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 876e692922942d8d2009387b5ca42df117181700..de824c1bc3d37d260bb11f28eba65c326338cd4c 100644 (file)
@@ -103,7 +103,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -376,6 +375,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -430,6 +430,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -450,6 +451,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -597,9 +599,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index aa59c242e7155ae3acdbe843d4f988431a6db852..071839ca6a5955a8a05f47bdc5acc9f3bf574e1f 100644 (file)
@@ -93,7 +93,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -358,6 +357,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -401,6 +401,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -421,6 +422,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -568,9 +570,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 308cd93929a9f6761b06782a2b032ab1e3565540..37ac7b019ec162e37dc1899353c3b1d248f8496f 100644 (file)
@@ -95,7 +95,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -359,6 +358,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -410,6 +410,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -430,6 +431,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -577,9 +579,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 0bc210ace87002232e6973d46b9e8103e26a3ee5..60877986626013be58b607b0d4f10e17de5b24c9 100644 (file)
@@ -94,7 +94,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -375,6 +374,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -432,6 +432,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -452,6 +453,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -599,9 +601,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 3b3b832dee805c8b3701d73d09c6b3c44b6ffe14..0abb53c38c20da1e13ccfbbd8e73f035b1acaaa0 100644 (file)
@@ -114,7 +114,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -419,6 +418,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -518,6 +518,7 @@ CONFIG_RTC_DRV_MSM6242=m
 CONFIG_RTC_DRV_RP5C01=m
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -538,6 +539,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -685,9 +687,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index e3633c66926f44ddb78dc524abf25ced4ca31b70..cb14c234d3adb3353a5e5e404bf3e86c5f498a2c 100644 (file)
@@ -92,7 +92,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -357,6 +356,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -400,6 +400,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -420,6 +421,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -567,9 +569,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 88b3f7f9f146a13e9d33f6b2b8705c873381a7f5..e8a1920aded799b0fa0c6deb746c78f1b920b6e2 100644 (file)
@@ -93,7 +93,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -358,6 +357,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -401,6 +401,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -421,6 +422,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -568,9 +570,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 3dd5b536921e75bbd8816691e47d37d7802cf4ed..2cbf416fc725b6ca815c319aea42565f10c07f1e 100644 (file)
@@ -94,7 +94,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -365,6 +364,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -419,6 +419,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -439,6 +440,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -586,9 +588,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index 715e015ed270f5ae114b0626395846e5b45f4baa..fed3cc7abcc447f53e66a11e884aedffa37429d8 100644 (file)
@@ -90,7 +90,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -355,6 +354,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -403,6 +403,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -423,6 +424,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -570,8 +572,10 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index f9ff129ac7c20031ce63080652238f25610c7055..0954fde256e6a5b5468ff1513147bcad82acc6d4 100644 (file)
@@ -90,7 +90,6 @@ CONFIG_NF_CONNTRACK_SANE=m
 CONFIG_NF_CONNTRACK_SIP=m
 CONFIG_NF_CONNTRACK_TFTP=m
 CONFIG_NF_TABLES=m
-CONFIG_NF_TABLES_SET=m
 CONFIG_NF_TABLES_INET=y
 CONFIG_NF_TABLES_NETDEV=y
 CONFIG_NFT_NUMGEN=m
@@ -355,6 +354,7 @@ CONFIG_IPVLAN=m
 CONFIG_IPVTAP=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_BAREUDP=m
 CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
@@ -402,6 +402,7 @@ CONFIG_RTC_CLASS=y
 # CONFIG_RTC_NVMEM is not set
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_DAX=m
 CONFIG_EXT4_FS=y
@@ -422,6 +423,7 @@ CONFIG_ZISOFS=y
 CONFIG_UDF_FS=m
 CONFIG_MSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_EXFAT_FS=m
 CONFIG_PROC_KCORE=y
 CONFIG_PROC_CHILDREN=y
 CONFIG_TMPFS=y
@@ -569,9 +571,11 @@ CONFIG_XZ_DEC_TEST=m
 CONFIG_STRING_SELFTEST=m
 # CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
 CONFIG_MAGIC_SYSRQ=y
+CONFIG_TEST_LOCKUP=m
 CONFIG_WW_MUTEX_SELFTEST=m
 CONFIG_EARLY_PRINTK=y
 CONFIG_TEST_LIST_SORT=m
+CONFIG_TEST_MIN_HEAP=m
 CONFIG_TEST_SORT=m
 CONFIG_REED_SOLOMON_TEST=m
 CONFIG_ATOMIC64_SELFTEST=m
index f9b94e4b94f97b9ca473f8679ed0dcec46fa829f..3f2c15d6f18c03bb3f73d3de21489dc9240811cf 100644 (file)
@@ -30,7 +30,8 @@ __wsum csum_partial(const void *buff, int len, __wsum sum);
  * better 64-bit) boundary
  */
 
-extern __wsum csum_partial_copy_from_user(const void __user *src,
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
+extern __wsum csum_and_copy_from_user(const void __user *src,
                                                void *dst,
                                                int len, __wsum sum,
                                                int *csum_err);
index 4892f314ff380e8e5a4d96e3d45522475036e731..e091e36d34648796230bbe5c8b51b3f30c35da86 100644 (file)
 #define MCFGPIO_IRQ_VECBASE    (MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
 #define MCFGPIO_PIN_MAX                87
 
+/*
+ * Phase Locked Loop (PLL)
+ */
+#define MCF_PLL_CR             0xFC0C0000
+#define MCF_PLL_DR             0xFC0C0004
+#define MCF_PLL_SR             0xFC0C0008
+
 /*
  *  DSPI module.
  */
 #define MCFEDMA_IRQ_INTR16     (MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
 #define MCFEDMA_IRQ_INTR56     (MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
 #define MCFEDMA_IRQ_ERR        (MCFINT0_VECBASE + MCFINT0_EDMA_ERR)
+/*
+ *  esdhc module.
+ */
+#define MCFSDHC_BASE           0xfc0cc000
+#define MCFSDHC_SIZE           256
+#define MCFINT2_SDHC           31
+#define MCF_IRQ_SDHC           (MCFINT2_VECBASE + MCFINT2_SDHC)
+#define MCFSDHC_CLK            (MCFSDHC_BASE + 0x2c)
 
 #endif /* m5441xsim_h */
index de1470c4d829bb23c10e036147ef7654c31ab1f5..1149251ea58d26d716f470c8f387993ecac6a98c 100644 (file)
@@ -257,6 +257,7 @@ extern int rbv_present,via_alt_mapping;
 
 struct irq_desc;
 
+extern void via_l2_flush(int writeback);
 extern void via_register_interrupts(void);
 extern void via_irq_enable(int);
 extern void via_irq_disable(int);
index 0aca504fae31c87aa8603f336bea7bfc4d5cab01..722627e06d6659a50dcbf715154142c791e62557 100644 (file)
@@ -30,6 +30,8 @@ extern struct clk_ops clk_ops0;
 extern struct clk_ops clk_ops1;
 #endif /* MCFPM_PPMCR1 */
 
+extern struct clk_ops clk_ops2;
+
 #define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
 static struct clk __clk_##clk_bank##_##clk_slot = { \
        .name = clk_name, \
index 7e85de984df197aebb6c56debfe56dbf1f3fc7fe..9ae9f8d05925f872c30b1d27b03a79a5199b2ed4 100644 (file)
@@ -142,7 +142,7 @@ asm volatile ("\n"                                  \
                __get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT);   \
                break;                                                  \
        case 8: {                                                       \
-               const void *__gu_ptr = (ptr);                           \
+               const void __user *__gu_ptr = (ptr);                    \
                union {                                                 \
                        u64 l;                                          \
                        __typeof__(*(ptr)) t;                           \
index f4f49fcb76d0fe3e7107cc9e68a70aabf34ccb27..f71b1bbcc1988c3e082b17b2d39520282930c1aa 100644 (file)
 435    common  clone3                          __sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 5fa3d392e181de41ef06a894a9b18d727be4fe26..31797be9a3dc49b4e4196032f7f68d71a1811789 100644 (file)
@@ -129,7 +129,7 @@ EXPORT_SYMBOL(csum_partial);
  */
 
 __wsum
-csum_partial_copy_from_user(const void __user *src, void *dst,
+csum_and_copy_from_user(const void __user *src, void *dst,
                            int len, __wsum sum, int *csum_err)
 {
        /*
@@ -316,7 +316,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
        return(sum);
 }
 
-EXPORT_SYMBOL(csum_partial_copy_from_user);
+EXPORT_SYMBOL(csum_and_copy_from_user);
 
 
 /*
index 611f73bfc87c95fe7fa0f514d38f4fb0161743eb..d0126ab01360b6dc68d7298d9b16f62b053eae2f 100644 (file)
@@ -59,7 +59,6 @@ extern void iop_preinit(void);
 extern void iop_init(void);
 extern void via_init(void);
 extern void via_init_clock(irq_handler_t func);
-extern void via_flush_cache(void);
 extern void oss_init(void);
 extern void psc_init(void);
 extern void baboon_init(void);
@@ -130,21 +129,6 @@ int __init mac_parse_bootinfo(const struct bi_record *record)
        return unknown;
 }
 
-/*
- * Flip into 24bit mode for an instant - flushes the L2 cache card. We
- * have to disable interrupts for this. Our IRQ handlers will crap
- * themselves if they take an IRQ in 24bit mode!
- */
-
-static void mac_cache_card_flush(int writeback)
-{
-       unsigned long flags;
-
-       local_irq_save(flags);
-       via_flush_cache();
-       local_irq_restore(flags);
-}
-
 void __init config_mac(void)
 {
        if (!MACH_IS_MAC)
@@ -175,9 +159,8 @@ void __init config_mac(void)
         * not.
         */
 
-       if (macintosh_config->ident == MAC_MODEL_IICI
-           || macintosh_config->ident == MAC_MODEL_IIFX)
-               mach_l2_flush = mac_cache_card_flush;
+       if (macintosh_config->ident == MAC_MODEL_IICI)
+               mach_l2_flush = via_l2_flush;
 }
 
 
index 9bfa170157688f1b16859e04d2dd0d3dec10cda8..d3775afb0f0762d12f3a69ef0efcc9022f77881d 100644 (file)
@@ -299,7 +299,6 @@ void __init iop_init(void)
 
 /*
  * Register the interrupt handler for the IOPs.
- * TODO: might be wrong for non-OSS machines. Anyone?
  */
 
 void __init iop_register_interrupts(void)
@@ -566,36 +565,42 @@ irqreturn_t iop_ism_irq(int irq, void *dev_id)
        uint iop_num = (uint) dev_id;
        volatile struct mac_iop *iop = iop_base[iop_num];
        int i,state;
+       u8 events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
 
        iop_pr_debug("status %02X\n", iop->status_ctrl);
 
-       /* INT0 indicates a state change on an outgoing message channel */
-
-       if (iop->status_ctrl & IOP_INT0) {
-               iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
-               iop_pr_debug("new status %02X, send states", iop->status_ctrl);
-               for (i = 0 ; i < NUM_IOP_CHAN  ; i++) {
-                       state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
-                       iop_pr_cont(" %02X", state);
-                       if (state == IOP_MSG_COMPLETE) {
-                               iop_handle_send(iop_num, i);
+       do {
+               /* INT0 indicates state change on an outgoing message channel */
+               if (events & IOP_INT0) {
+                       iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
+                       iop_pr_debug("new status %02X, send states",
+                                    iop->status_ctrl);
+                       for (i = 0; i < NUM_IOP_CHAN; i++) {
+                               state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
+                               iop_pr_cont(" %02X", state);
+                               if (state == IOP_MSG_COMPLETE)
+                                       iop_handle_send(iop_num, i);
                        }
+                       iop_pr_cont("\n");
                }
-               iop_pr_cont("\n");
-       }
 
-       if (iop->status_ctrl & IOP_INT1) {      /* INT1 for incoming msgs */
-               iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
-               iop_pr_debug("new status %02X, recv states", iop->status_ctrl);
-               for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
-                       state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
-                       iop_pr_cont(" %02X", state);
-                       if (state == IOP_MSG_NEW) {
-                               iop_handle_recv(iop_num, i);
+               /* INT1 for incoming messages */
+               if (events & IOP_INT1) {
+                       iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
+                       iop_pr_debug("new status %02X, recv states",
+                                    iop->status_ctrl);
+                       for (i = 0; i < NUM_IOP_CHAN; i++) {
+                               state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
+                               iop_pr_cont(" %02X", state);
+                               if (state == IOP_MSG_NEW)
+                                       iop_handle_recv(iop_num, i);
                        }
+                       iop_pr_cont("\n");
                }
-               iop_pr_cont("\n");
-       }
+
+               events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
+       } while (events);
+
        return IRQ_HANDLED;
 }
 
index 3c2cfcb749825f183b28c77a48c64c6ada9ff03b..1f0fad2a98a07dd437a1486fb18f09cc48558f55 100644 (file)
@@ -294,10 +294,14 @@ void via_debug_dump(void)
  * the system into 24-bit mode for an instant.
  */
 
-void via_flush_cache(void)
+void via_l2_flush(int writeback)
 {
+       unsigned long flags;
+
+       local_irq_save(flags);
        via2[gBufB] &= ~VIA2B_vMode32;
        via2[gBufB] |= VIA2B_vMode32;
+       local_irq_restore(flags);
 }
 
 /*
index 7340f5b6cf6d55dbafe7b86fbf18ff58f4f6ada3..f8005a7efb0be96b0b59f3bc61c771de19197fe1 100644 (file)
@@ -34,7 +34,7 @@ struct savekmsg {
     u_long magic2;     /* SAVEKMSG_MAGIC2 */
     u_long magicptr;   /* address of magic1 */
     u_long size;
-    char data[0];
+    char data[];
 };
 
 
index 92e12c2c2ec1f758b4674b6aa56be987a2018594..51c43ee5e380bbf5609fa07c6e8006c542ea7e4a 100644 (file)
@@ -6,7 +6,6 @@
 
 #include <linux/export.h>
 #include <linux/string.h>
-#include <linux/cryptohash.h>
 #include <linux/delay.h>
 #include <linux/in6.h>
 #include <linux/syscalls.h>
index 4c67b11f9c9ef83f70fb3b7cb891dbe81e271564..edacc4561f2b3bd30f28c86328da667c4ae63049 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index d1ed066e1a17790970a641fb542b26c831e02667..8c8ea139653ed3830ec47cbc5bdf442f0730559d 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <asm/byteorder.h>
-#include <linux/cryptohash.h>
 #include <asm/octeon/octeon.h>
 #include <crypto/internal/hash.h>
 
index 1f9e8ad636cce34a128d143e604b6847caae0438..f777141f52568fcac820d2ab9bfa5a7075d32314 100644 (file)
 435    n32     clone3                          __sys_clone3
 437    n32     openat2                         sys_openat2
 438    n32     pidfd_getfd                     sys_pidfd_getfd
+439    n32     faccessat2                      sys_faccessat2
index c0b9d802dbf6dd82c9b4be2a799871a5df8ef3db..da8c76394e178fc4ca26522684eee4e5ac0a4a2a 100644 (file)
 435    n64     clone3                          __sys_clone3
 437    n64     openat2                         sys_openat2
 438    n64     pidfd_getfd                     sys_pidfd_getfd
+439    n64     faccessat2                      sys_faccessat2
index ac586774c980537ed69d85ab4850adb79cd2c9e8..13280625d312e98dcebdaafaca210a353c4a43d9 100644 (file)
 435    o32     clone3                          __sys_clone3
 437    o32     openat2                         sys_openat2
 438    o32     pidfd_getfd                     sys_pidfd_getfd
+439    o32     faccessat2                      sys_faccessat2
index 703c5ee634218914bd0fb87d7138c6a6c9d61713..ec39698d3beac86c3948a575173c0a057bfdf335 100644 (file)
@@ -14,8 +14,6 @@
 extern __wsum csum_partial(const void *buff, int len, __wsum sum);
 extern __wsum csum_partial_copy(const void *src, void *dst, int len,
                                __wsum sum);
-extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
-                                       int len, __wsum sum, int *csum_err);
 #define csum_partial_copy_nocheck(src, dst, len, sum)  \
        csum_partial_copy((src), (dst), (len), (sum))
 
index c1c22819a04d10112e48e3e5dd911f0c2515cd00..fe8c63b2d2c3c42a66ae6a4e590753c8f948dd3f 100644 (file)
@@ -26,13 +26,6 @@ extern __wsum csum_partial(const void *, int, __wsum);
  */
 extern __wsum csum_partial_copy_nocheck(const void *, void *, int, __wsum);
 
-/*
- * this is a new version of the above that records errors it finds in *errp,
- * but continues and zeros the rest of the buffer.
- */
-extern __wsum csum_partial_copy_from_user(const void __user *src,
-               void *dst, int len, __wsum sum, int *errp);
-
 /*
  *     Optimized for IP headers, which always checksum on 4 octet boundaries.
  *
index 9832c73a70211dd4a9951c2fbd203cac911f2db3..cd7df48dc874cde101c489692c423f8e971a4970 100644 (file)
@@ -93,10 +93,8 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
 
 #define set_pte_at(mm, addr, ptep, pteval)                     \
        do {                                                    \
-               pte_t old_pte;                                  \
                unsigned long flags;                            \
                spin_lock_irqsave(pgd_spinlock((mm)->pgd), flags);\
-               old_pte = *ptep;                                \
                set_pte(ptep, pteval);                          \
                purge_tlb_entries(mm, addr);                    \
                spin_unlock_irqrestore(pgd_spinlock((mm)->pgd), flags);\
index e1a8fee3ad491f62424c02769769235d15a0297e..d46b6709ec56c415ec9d6965ff9ae1de52b0e3f9 100644 (file)
@@ -300,7 +300,7 @@ static ssize_t perf_write(struct file *file, const char __user *buf,
        else
                return -EFAULT;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EACCES;
 
        if (count != sizeof(uint32_t))
index 52a15f5cd1308d222a821a231244d0471ab7be07..5a758fa6ec52427615cb8a0dfbfc938b4dc0d96c 100644 (file)
 435    common  clone3                          sys_clone3_wrapper
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 256322c7b648e1b3d10c06ffdc4652cb8f3ecd68..c6f161583549fcb8cbed707472bc763368ee5b22 100644 (file)
@@ -123,23 +123,3 @@ __wsum csum_partial_copy_nocheck(const void *src, void *dst,
        return sum;
 }
 EXPORT_SYMBOL(csum_partial_copy_nocheck);
-
-/*
- * Copy from userspace and compute checksum.  If we catch an exception
- * then zero the rest of the buffer.
- */
-__wsum csum_partial_copy_from_user(const void __user *src,
-                                       void *dst, int len,
-                                       __wsum sum, int *err_ptr)
-{
-       int missing;
-
-       missing = copy_from_user(dst, src, len);
-       if (missing) {
-               memset(dst + len - missing, 0, missing);
-               *err_ptr = -EFAULT;
-       }
-               
-       return csum_partial(dst, len, sum);
-}
-EXPORT_SYMBOL(csum_partial_copy_from_user);
index 5224fb38d766d5e76ed6055c80a8c31ce1b17df4..01d7071b23f7ad3f70736dd0ae88fbca0fcaeed1 100644 (file)
@@ -562,7 +562,7 @@ void __init mem_init(void)
                        > BITS_PER_LONG);
 
        high_memory = __va((max_pfn << PAGE_SHIFT));
-       set_max_mapnr(page_to_pfn(virt_to_page(high_memory - 1)) + 1);
+       set_max_mapnr(max_low_pfn);
        memblock_free_all();
 
 #ifdef CONFIG_PA11
index d13b5328ca108f128c1718df55caf04ae12049c7..b29d7cb38368bf2ea88254e2ca0ca2b2564a2385 100644 (file)
@@ -126,6 +126,7 @@ config PPC
        select ARCH_HAS_MMIOWB                  if PPC64
        select ARCH_HAS_PHYS_TO_DMA
        select ARCH_HAS_PMEM_API
+       select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
        select ARCH_HAS_PTE_DEVMAP              if PPC_BOOK3S_64
        select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_MEMBARRIER_CALLBACKS
index 7d1bf2fcf668961adecd51a16a7b295898fbbe65..c24f605033bdb336e9350d3149f8ea33daf700cb 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/md5.h>
 #include <asm/byteorder.h>
index 6379990bd6044e4992978d1b1bb4550fc85de4dc..cb57be4ada61cdce3b2e267fd3b40b288652d86b 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <asm/byteorder.h>
index 7b43fc352089b1a2cf0f934b91b2d02b76f63421..b40dc50a6908aeb3be8f7e8bb08d75b076e70863 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <asm/byteorder.h>
 
-extern void powerpc_sha_transform(u32 *state, const u8 *src, u32 *temp);
+void powerpc_sha_transform(u32 *state, const u8 *src);
 
-static int sha1_init(struct shash_desc *desc)
+static int powerpc_sha1_init(struct shash_desc *desc)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
 
@@ -34,8 +33,8 @@ static int sha1_init(struct shash_desc *desc)
        return 0;
 }
 
-static int sha1_update(struct shash_desc *desc, const u8 *data,
-                       unsigned int len)
+static int powerpc_sha1_update(struct shash_desc *desc, const u8 *data,
+                              unsigned int len)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
        unsigned int partial, done;
@@ -47,7 +46,6 @@ static int sha1_update(struct shash_desc *desc, const u8 *data,
        src = data;
 
        if ((partial + len) > 63) {
-               u32 temp[SHA_WORKSPACE_WORDS];
 
                if (partial) {
                        done = -partial;
@@ -56,12 +54,11 @@ static int sha1_update(struct shash_desc *desc, const u8 *data,
                }
 
                do {
-                       powerpc_sha_transform(sctx->state, src, temp);
+                       powerpc_sha_transform(sctx->state, src);
                        done += 64;
                        src = data + done;
                } while (done + 63 < len);
 
-               memzero_explicit(temp, sizeof(temp));
                partial = 0;
        }
        memcpy(sctx->buffer + partial, src, len - done);
@@ -71,7 +68,7 @@ static int sha1_update(struct shash_desc *desc, const u8 *data,
 
 
 /* Add padding and return the message digest. */
-static int sha1_final(struct shash_desc *desc, u8 *out)
+static int powerpc_sha1_final(struct shash_desc *desc, u8 *out)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
        __be32 *dst = (__be32 *)out;
@@ -84,10 +81,10 @@ static int sha1_final(struct shash_desc *desc, u8 *out)
        /* Pad out to 56 mod 64 */
        index = sctx->count & 0x3f;
        padlen = (index < 56) ? (56 - index) : ((64+56) - index);
-       sha1_update(desc, padding, padlen);
+       powerpc_sha1_update(desc, padding, padlen);
 
        /* Append length */
-       sha1_update(desc, (const u8 *)&bits, sizeof(bits));
+       powerpc_sha1_update(desc, (const u8 *)&bits, sizeof(bits));
 
        /* Store state in digest */
        for (i = 0; i < 5; i++)
@@ -99,7 +96,7 @@ static int sha1_final(struct shash_desc *desc, u8 *out)
        return 0;
 }
 
-static int sha1_export(struct shash_desc *desc, void *out)
+static int powerpc_sha1_export(struct shash_desc *desc, void *out)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
 
@@ -107,7 +104,7 @@ static int sha1_export(struct shash_desc *desc, void *out)
        return 0;
 }
 
-static int sha1_import(struct shash_desc *desc, const void *in)
+static int powerpc_sha1_import(struct shash_desc *desc, const void *in)
 {
        struct sha1_state *sctx = shash_desc_ctx(desc);
 
@@ -117,11 +114,11 @@ static int sha1_import(struct shash_desc *desc, const void *in)
 
 static struct shash_alg alg = {
        .digestsize     =       SHA1_DIGEST_SIZE,
-       .init           =       sha1_init,
-       .update         =       sha1_update,
-       .final          =       sha1_final,
-       .export         =       sha1_export,
-       .import         =       sha1_import,
+       .init           =       powerpc_sha1_init,
+       .update         =       powerpc_sha1_update,
+       .final          =       powerpc_sha1_final,
+       .export         =       powerpc_sha1_export,
+       .import         =       powerpc_sha1_import,
        .descsize       =       sizeof(struct sha1_state),
        .statesize      =       sizeof(struct sha1_state),
        .base           =       {
index 84939e563b817ef1206d3ab7f69562a881eee8d0..ceb0b6c980b3bb31e9f4e43618cebb5350709aa6 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <asm/byteorder.h>
index 635969b5b58e529c875c10cdce5f112537b79728..13f90dd03450a4608ed235b7b14869a9cdfb7885 100644 (file)
@@ -699,10 +699,6 @@ static inline void iosync(void)
  *
  * * iounmap undoes such a mapping and can be hooked
  *
- * * __ioremap_at (and the pending __iounmap_at) are low level functions to
- *   create hand-made mappings for use only by the PCI code and cannot
- *   currently be hooked. Must be page aligned.
- *
  * * __ioremap_caller is the same as above but takes an explicit caller
  *   reference rather than using __builtin_return_address(0)
  *
@@ -719,6 +715,8 @@ void __iomem *ioremap_coherent(phys_addr_t address, unsigned long size);
 
 extern void iounmap(volatile void __iomem *addr);
 
+void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size);
+
 int early_ioremap_range(unsigned long ea, phys_addr_t pa,
                        unsigned long size, pgprot_t prot);
 void __iomem *do_ioremap(phys_addr_t pa, phys_addr_t offset, unsigned long size,
@@ -727,10 +725,6 @@ void __iomem *do_ioremap(phys_addr_t pa, phys_addr_t offset, unsigned long size,
 extern void __iomem *__ioremap_caller(phys_addr_t, unsigned long size,
                                      pgprot_t prot, void *caller);
 
-extern void __iomem * __ioremap_at(phys_addr_t pa, void *ea,
-                                  unsigned long size, pgprot_t prot);
-extern void __iounmap_at(void *ea, unsigned long size);
-
 /*
  * When CONFIG_PPC_INDIRECT_PIO is set, we use the generic iomap implementation
  * which needs some additional definitions here. They basically allow PIO
index 69f4cb3b7c56c2c009c6285f8ef46050db1e9a42..b92e81b256e58172bb795da36fb1daf2eebbbe63 100644 (file)
@@ -66,7 +66,7 @@ struct pci_controller {
 
        void __iomem *io_base_virt;
 #ifdef CONFIG_PPC64
-       void *io_base_alloc;
+       void __iomem *io_base_alloc;
 #endif
        resource_size_t io_base_phys;
        resource_size_t pci_io_size;
index be48c2215fa2270be027e2458d60f1d271c2bf38..a809b1b44ddfe4a72595914278293f0c21ea6d0d 100644 (file)
@@ -31,7 +31,7 @@
  * Struct fields are always 32 or 64 bit aligned, depending on them being 32
  * or 64 bit wide respectively.
  *
- * See Documentation/virt/kvm/ppc-pv.txt
+ * See Documentation/virt/kvm/ppc-pv.rst
  */
 struct kvm_vcpu_arch_shared {
        __u64 scratch1;
index 1c4385852d3ddb2bdc77ada8fcb9e4ea75ca9662..244542ae2a915698a246df455f9416040e7ed163 100644 (file)
@@ -162,6 +162,9 @@ UBSAN_SANITIZE_kprobes.o := n
 GCOV_PROFILE_kprobes-ftrace.o := n
 KCOV_INSTRUMENT_kprobes-ftrace.o := n
 UBSAN_SANITIZE_kprobes-ftrace.o := n
+GCOV_PROFILE_syscall_64.o := n
+KCOV_INSTRUMENT_syscall_64.o := n
+UBSAN_SANITIZE_syscall_64.o := n
 UBSAN_SANITIZE_vdso.o := n
 
 # Necessary for booting with kcov enabled on book3e machines
index b0ad930cbae5caa2b39f581dea5e999740a95e00..ebeebab74b564b7960a5270f180721d430d04dd0 100644 (file)
@@ -2411,6 +2411,7 @@ EXC_COMMON_BEGIN(facility_unavailable_common)
        GEN_COMMON facility_unavailable
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      facility_unavailable_exception
+       REST_NVGPRS(r1) /* instruction emulation may change GPRs */
        b       interrupt_return
 
        GEN_KVM facility_unavailable
@@ -2440,6 +2441,7 @@ EXC_COMMON_BEGIN(h_facility_unavailable_common)
        GEN_COMMON h_facility_unavailable
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      facility_unavailable_exception
+       REST_NVGPRS(r1) /* XXX Shouldn't be necessary in practice */
        b       interrupt_return
 
        GEN_KVM h_facility_unavailable
index 1f1169856dc848ab7325f6b8cfe91d498c84b27f..112d150354b2082a568a24521516c96c44001e1d 100644 (file)
@@ -748,9 +748,8 @@ void do_IRQ(struct pt_regs *regs)
 
 static void *__init alloc_vm_stack(void)
 {
-       return __vmalloc_node_range(THREAD_SIZE, THREAD_ALIGN, VMALLOC_START,
-                                   VMALLOC_END, THREADINFO_GFP, PAGE_KERNEL,
-                                    0, NUMA_NO_NODE, (void*)_RET_IP_);
+       return __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, THREADINFO_GFP,
+                             NUMA_NO_NODE, (void *)_RET_IP_);
 }
 
 static void __init vmap_irqstack_init(void)
index 773671b512df71ddad08636cc705f7c112565ba3..2257d24e6a26647a232ddf11629254c2c69e33d0 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/notifier.h>
+#include <linux/vmalloc.h>
 
 #include <asm/processor.h>
 #include <asm/io.h>
@@ -38,6 +39,22 @@ EXPORT_SYMBOL_GPL(isa_bridge_pcidev);
 #define ISA_SPACE_MASK 0x1
 #define ISA_SPACE_IO 0x1
 
+static void remap_isa_base(phys_addr_t pa, unsigned long size)
+{
+       WARN_ON_ONCE(ISA_IO_BASE & ~PAGE_MASK);
+       WARN_ON_ONCE(pa & ~PAGE_MASK);
+       WARN_ON_ONCE(size & ~PAGE_MASK);
+
+       if (slab_is_available()) {
+               if (ioremap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
+                               pgprot_noncached(PAGE_KERNEL)))
+                       unmap_kernel_range(ISA_IO_BASE, size);
+       } else {
+               early_ioremap_range(ISA_IO_BASE, pa, size,
+                               pgprot_noncached(PAGE_KERNEL));
+       }
+}
+
 static void pci_process_ISA_OF_ranges(struct device_node *isa_node,
                                      unsigned long phb_io_base_phys)
 {
@@ -105,15 +122,13 @@ static void pci_process_ISA_OF_ranges(struct device_node *isa_node,
        if (size > 0x10000)
                size = 0x10000;
 
-       __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE,
-                    size, pgprot_noncached(PAGE_KERNEL));
+       remap_isa_base(phb_io_base_phys, size);
        return;
 
 inval_range:
        printk(KERN_ERR "no ISA IO ranges or unexpected isa range, "
               "mapping 64k\n");
-       __ioremap_at(phb_io_base_phys, (void *)ISA_IO_BASE,
-                    0x10000, pgprot_noncached(PAGE_KERNEL));
+       remap_isa_base(phb_io_base_phys, 0x10000);
 }
 
 
@@ -248,8 +263,7 @@ void __init isa_bridge_init_non_pci(struct device_node *np)
         * and map it
         */
        isa_io_base = ISA_IO_BASE;
-       __ioremap_at(pbase, (void *)ISA_IO_BASE,
-                    size, pgprot_noncached(PAGE_KERNEL));
+       remap_isa_base(pbase, size);
 
        pr_debug("ISA: Non-PCI bridge is %pOF\n", np);
 }
@@ -297,7 +311,7 @@ static void isa_bridge_remove(void)
        isa_bridge_pcidev = NULL;
 
        /* Unmap the ISA area */
-       __iounmap_at((void *)ISA_IO_BASE, 0x10000);
+       unmap_kernel_range(ISA_IO_BASE, 0x10000);
 }
 
 /**
index fb4f61096613217503c089561cff8ea85575f2c2..0cd1c88bfc8b6c20dc77280ad78637a87470968e 100644 (file)
@@ -655,9 +655,7 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
        int rc = -1;
 
        switch (reason) {
-       case KMSG_DUMP_RESTART:
-       case KMSG_DUMP_HALT:
-       case KMSG_DUMP_POWEROFF:
+       case KMSG_DUMP_SHUTDOWN:
                /* These are almost always orderly shutdowns. */
                return;
        case KMSG_DUMP_OOPS:
index f83d1f69b1dd837b1d098a46098d4f1b83fe3535..d9ac980c398c81ac97579f8c7516b19591bdcf6a 100644 (file)
@@ -109,23 +109,47 @@ int pcibios_unmap_io_space(struct pci_bus *bus)
        /* Get the host bridge */
        hose = pci_bus_to_host(bus);
 
-       /* Check if we have IOs allocated */
-       if (hose->io_base_alloc == NULL)
-               return 0;
-
        pr_debug("IO unmapping for PHB %pOF\n", hose->dn);
        pr_debug("  alloc=0x%p\n", hose->io_base_alloc);
 
-       /* This is a PHB, we fully unmap the IO area */
-       vunmap(hose->io_base_alloc);
-
+       iounmap(hose->io_base_alloc);
        return 0;
 }
 EXPORT_SYMBOL_GPL(pcibios_unmap_io_space);
 
-static int pcibios_map_phb_io_space(struct pci_controller *hose)
+void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size)
 {
        struct vm_struct *area;
+       unsigned long addr;
+
+       WARN_ON_ONCE(paddr & ~PAGE_MASK);
+       WARN_ON_ONCE(size & ~PAGE_MASK);
+
+       /*
+        * Let's allocate some IO space for that guy. We don't pass VM_IOREMAP
+        * because we don't care about alignment tricks that the core does in
+        * that case.  Maybe we should due to stupid card with incomplete
+        * address decoding but I'd rather not deal with those outside of the
+        * reserved 64K legacy region.
+        */
+       area = __get_vm_area_caller(size, 0, PHB_IO_BASE, PHB_IO_END,
+                                   __builtin_return_address(0));
+       if (!area)
+               return NULL;
+
+       addr = (unsigned long)area->addr;
+       if (ioremap_page_range(addr, addr + size, paddr,
+                       pgprot_noncached(PAGE_KERNEL))) {
+               unmap_kernel_range(addr, size);
+               return NULL;
+       }
+
+       return (void __iomem *)addr;
+}
+EXPORT_SYMBOL_GPL(ioremap_phb);
+
+static int pcibios_map_phb_io_space(struct pci_controller *hose)
+{
        unsigned long phys_page;
        unsigned long size_page;
        unsigned long io_virt_offset;
@@ -146,12 +170,11 @@ static int pcibios_map_phb_io_space(struct pci_controller *hose)
         * with incomplete address decoding but I'd rather not deal with
         * those outside of the reserved 64K legacy region.
         */
-       area = __get_vm_area(size_page, 0, PHB_IO_BASE, PHB_IO_END);
-       if (area == NULL)
+       hose->io_base_alloc = ioremap_phb(phys_page, size_page);
+       if (!hose->io_base_alloc)
                return -ENOMEM;
-       hose->io_base_alloc = area->addr;
-       hose->io_base_virt = (void __iomem *)(area->addr +
-                                             hose->io_base_phys - phys_page);
+       hose->io_base_virt = hose->io_base_alloc +
+                               hose->io_base_phys - phys_page;
 
        pr_debug("IO mapping for PHB %pOF\n", hose->dn);
        pr_debug("  phys=0x%016llx, virt=0x%p (alloc=0x%p)\n",
@@ -159,11 +182,6 @@ static int pcibios_map_phb_io_space(struct pci_controller *hose)
        pr_debug("  size=0x%016llx (alloc=0x%016lx)\n",
                 hose->pci_io_size, size_page);
 
-       /* Establish the mapping */
-       if (__ioremap_at(phys_page, area->addr, size_page,
-                        pgprot_noncached(PAGE_KERNEL)) == NULL)
-               return -ENOMEM;
-
        /* Fixup hose IO resource */
        io_virt_offset = pcibios_io_space_offset(hose);
        hose->io_resource.start += io_virt_offset;
index 220ae11555f2e1d739a8d0faefd2f416c35b00b2..f833a319082247ecc302f9bd25806c7677a64221 100644 (file)
 435    spu     clone3                          sys_ni_syscall
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 3fca22276bb10c16b2a84dea2ecfa0c8e2360267..b44dd75de5170959c07f70723781d15db389b1ee 100644 (file)
@@ -441,15 +441,9 @@ nonrecoverable:
 void system_reset_exception(struct pt_regs *regs)
 {
        unsigned long hsrr0, hsrr1;
-       bool nested = in_nmi();
        bool saved_hsrrs = false;
 
-       /*
-        * Avoid crashes in case of nested NMI exceptions. Recoverability
-        * is determined by RI and in_nmi
-        */
-       if (!nested)
-               nmi_enter();
+       nmi_enter();
 
        /*
         * System reset can interrupt code where HSRRs are live and MSR[RI]=1.
@@ -521,8 +515,7 @@ out:
                mtspr(SPRN_HSRR1, hsrr1);
        }
 
-       if (!nested)
-               nmi_exit();
+       nmi_exit();
 
        /* What should we do here? We could issue a shutdown or hard reset. */
 }
@@ -823,9 +816,8 @@ int machine_check_generic(struct pt_regs *regs)
 void machine_check_exception(struct pt_regs *regs)
 {
        int recover = 0;
-       bool nested = in_nmi();
-       if (!nested)
-               nmi_enter();
+
+       nmi_enter();
 
        __this_cpu_inc(irq_stat.mce_exceptions);
 
@@ -851,8 +843,7 @@ void machine_check_exception(struct pt_regs *regs)
        if (check_io_access(regs))
                goto bail;
 
-       if (!nested)
-               nmi_exit();
+       nmi_exit();
 
        die("Machine check", regs, SIGBUS);
 
@@ -863,8 +854,7 @@ void machine_check_exception(struct pt_regs *regs)
        return;
 
 bail:
-       if (!nested)
-               nmi_exit();
+       nmi_exit();
 }
 
 void SMIException(struct pt_regs *regs)
index 31a0f201fb6f43a315b6d6338eeb380d9a9bde1f..a1706b63b82de2ecaef7d212b469482683dfaa04 100644 (file)
@@ -90,6 +90,7 @@ SECTIONS
 #ifdef CONFIG_PPC64
                *(.tramp.ftrace.text);
 #endif
+               NOINSTR_TEXT
                SCHED_TEXT
                CPUIDLE_TEXT
                LOCK_TEXT
index 50a99d9684f7d81214b56f1ebe73bb87ceb41156..ba5cbb0d66bd6bc366ba5a3949cb82fb15f71bd6 100644 (file)
@@ -4,56 +4,6 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 
-/**
- * Low level function to establish the page tables for an IO mapping
- */
-void __iomem *__ioremap_at(phys_addr_t pa, void *ea, unsigned long size, pgprot_t prot)
-{
-       int ret;
-       unsigned long va = (unsigned long)ea;
-
-       /* We don't support the 4K PFN hack with ioremap */
-       if (pgprot_val(prot) & H_PAGE_4K_PFN)
-               return NULL;
-
-       if ((ea + size) >= (void *)IOREMAP_END) {
-               pr_warn("Outside the supported range\n");
-               return NULL;
-       }
-
-       WARN_ON(pa & ~PAGE_MASK);
-       WARN_ON(((unsigned long)ea) & ~PAGE_MASK);
-       WARN_ON(size & ~PAGE_MASK);
-
-       if (slab_is_available()) {
-               ret = ioremap_page_range(va, va + size, pa, prot);
-               if (ret)
-                       unmap_kernel_range(va, size);
-       } else {
-               ret = early_ioremap_range(va, pa, size, prot);
-       }
-
-       if (ret)
-               return NULL;
-
-       return (void __iomem *)ea;
-}
-EXPORT_SYMBOL(__ioremap_at);
-
-/**
- * Low level function to tear down the page tables for an IO mapping. This is
- * used for mappings that are manipulated manually, like partial unmapping of
- * PCI IOs or ISA space.
- */
-void __iounmap_at(void *ea, unsigned long size)
-{
-       WARN_ON(((unsigned long)ea) & ~PAGE_MASK);
-       WARN_ON(size & ~PAGE_MASK);
-
-       unmap_kernel_range((unsigned long)ea, size);
-}
-EXPORT_SYMBOL(__iounmap_at);
-
 void __iomem *__ioremap_caller(phys_addr_t addr, unsigned long size,
                               pgprot_t prot, void *caller)
 {
index eb82dda884e51ee22ae2ef96c798a4f9c7e7205a..0edcfd0b491d9892d5df6ff8f57f015dc88436dd 100644 (file)
@@ -976,7 +976,7 @@ static int thread_imc_event_init(struct perf_event *event)
        if (event->attr.type != event->pmu->type)
                return -ENOENT;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EACCES;
 
        /* Sampling not supported */
@@ -1412,7 +1412,7 @@ static int trace_imc_event_init(struct perf_event *event)
        if (event->attr.type != event->pmu->type)
                return -ENOENT;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EACCES;
 
        /* Return if this is a couting event */
index 8b3296b62f651fee604012652def96a7ecffd6e1..3b75e8f60609cbd0fd7cd2416c587c387ed57de7 100644 (file)
 
 #include "spufs.h"
 
-static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer,
-                               size_t size, loff_t *off)
-{
-       u64 data;
-       int ret;
-
-       if (spufs_coredump_read[num].read)
-               return spufs_coredump_read[num].read(ctx, buffer, size, off);
-
-       data = spufs_coredump_read[num].get(ctx);
-       ret = snprintf(buffer, size, "0x%.16llx", data);
-       if (ret >= size)
-               return size;
-       return ++ret; /* count trailing NULL */
-}
-
 static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
 {
        int i, sz, total = 0;
@@ -118,58 +102,43 @@ int spufs_coredump_extra_notes_size(void)
 static int spufs_arch_write_note(struct spu_context *ctx, int i,
                                  struct coredump_params *cprm, int dfd)
 {
-       loff_t pos = 0;
-       int sz, rc, total = 0;
-       const int bufsz = PAGE_SIZE;
-       char *name;
-       char fullname[80], *buf;
+       size_t sz = spufs_coredump_read[i].size;
+       char fullname[80];
        struct elf_note en;
-       size_t skip;
-
-       buf = (void *)get_zeroed_page(GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       size_t ret;
 
-       name = spufs_coredump_read[i].name;
-       sz = spufs_coredump_read[i].size;
-
-       sprintf(fullname, "SPU/%d/%s", dfd, name);
+       sprintf(fullname, "SPU/%d/%s", dfd, spufs_coredump_read[i].name);
        en.n_namesz = strlen(fullname) + 1;
        en.n_descsz = sz;
        en.n_type = NT_SPU;
 
        if (!dump_emit(cprm, &en, sizeof(en)))
-               goto Eio;
-
+               return -EIO;
        if (!dump_emit(cprm, fullname, en.n_namesz))
-               goto Eio;
-
+               return -EIO;
        if (!dump_align(cprm, 4))
-               goto Eio;
-
-       do {
-               rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
-               if (rc > 0) {
-                       if (!dump_emit(cprm, buf, rc))
-                               goto Eio;
-                       total += rc;
-               }
-       } while (rc == bufsz && total < sz);
-
-       if (rc < 0)
-               goto out;
-
-       skip = roundup(cprm->pos - total + sz, 4) - cprm->pos;
-       if (!dump_skip(cprm, skip))
-               goto Eio;
-
-       rc = 0;
-out:
-       free_page((unsigned long)buf);
-       return rc;
-Eio:
-       free_page((unsigned long)buf);
-       return -EIO;
+               return -EIO;
+
+       if (spufs_coredump_read[i].dump) {
+               ret = spufs_coredump_read[i].dump(ctx, cprm);
+               if (ret < 0)
+                       return ret;
+       } else {
+               char buf[32];
+
+               ret = snprintf(buf, sizeof(buf), "0x%.16llx",
+                              spufs_coredump_read[i].get(ctx));
+               if (ret >= sizeof(buf))
+                       return sizeof(buf);
+
+               /* count trailing the NULL: */
+               if (!dump_emit(cprm, buf, ret + 1))
+                       return -EIO;
+       }
+
+       if (!dump_skip(cprm, roundup(cprm->pos - ret + sz, 4) - cprm->pos))
+               return -EIO;
+       return 0;
 }
 
 int spufs_coredump_extra_notes_write(struct coredump_params *cprm)
index c0f950a3f4e1f556d7b2d32dc8b0db00333d8a18..e44427c245850c716c75af0ffe2b8882549f67d6 100644 (file)
@@ -9,6 +9,7 @@
 
 #undef DEBUG
 
+#include <linux/coredump.h>
 #include <linux/fs.h>
 #include <linux/ioctl.h>
 #include <linux/export.h>
@@ -129,6 +130,14 @@ out:
        return ret;
 }
 
+static ssize_t spufs_dump_emit(struct coredump_params *cprm, void *buf,
+               size_t size)
+{
+       if (!dump_emit(cprm, buf, size))
+               return -EIO;
+       return size;
+}
+
 #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)     \
 static int __fops ## _open(struct inode *inode, struct file *file)     \
 {                                                                      \
@@ -172,12 +181,9 @@ spufs_mem_release(struct inode *inode, struct file *file)
 }
 
 static ssize_t
-__spufs_mem_read(struct spu_context *ctx, char __user *buffer,
-                       size_t size, loff_t *pos)
+spufs_mem_dump(struct spu_context *ctx, struct coredump_params *cprm)
 {
-       char *local_store = ctx->ops->get_ls(ctx);
-       return simple_read_from_buffer(buffer, size, pos, local_store,
-                                       LS_SIZE);
+       return spufs_dump_emit(cprm, ctx->ops->get_ls(ctx), LS_SIZE);
 }
 
 static ssize_t
@@ -190,7 +196,8 @@ spufs_mem_read(struct file *file, char __user *buffer,
        ret = spu_acquire(ctx);
        if (ret)
                return ret;
-       ret = __spufs_mem_read(ctx, buffer, size, pos);
+       ret = simple_read_from_buffer(buffer, size, pos, ctx->ops->get_ls(ctx),
+                                     LS_SIZE);
        spu_release(ctx);
 
        return ret;
@@ -459,12 +466,10 @@ spufs_regs_open(struct inode *inode, struct file *file)
 }
 
 static ssize_t
-__spufs_regs_read(struct spu_context *ctx, char __user *buffer,
-                       size_t size, loff_t *pos)
+spufs_regs_dump(struct spu_context *ctx, struct coredump_params *cprm)
 {
-       struct spu_lscsa *lscsa = ctx->csa.lscsa;
-       return simple_read_from_buffer(buffer, size, pos,
-                                     lscsa->gprs, sizeof lscsa->gprs);
+       return spufs_dump_emit(cprm, ctx->csa.lscsa->gprs,
+                              sizeof(ctx->csa.lscsa->gprs));
 }
 
 static ssize_t
@@ -482,7 +487,8 @@ spufs_regs_read(struct file *file, char __user *buffer,
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
-       ret = __spufs_regs_read(ctx, buffer, size, pos);
+       ret = simple_read_from_buffer(buffer, size, pos, ctx->csa.lscsa->gprs,
+                                     sizeof(ctx->csa.lscsa->gprs));
        spu_release_saved(ctx);
        return ret;
 }
@@ -517,12 +523,10 @@ static const struct file_operations spufs_regs_fops = {
 };
 
 static ssize_t
-__spufs_fpcr_read(struct spu_context *ctx, char __user * buffer,
-                       size_t size, loff_t * pos)
+spufs_fpcr_dump(struct spu_context *ctx, struct coredump_params *cprm)
 {
-       struct spu_lscsa *lscsa = ctx->csa.lscsa;
-       return simple_read_from_buffer(buffer, size, pos,
-                                     &lscsa->fpcr, sizeof(lscsa->fpcr));
+       return spufs_dump_emit(cprm, &ctx->csa.lscsa->fpcr,
+                              sizeof(ctx->csa.lscsa->fpcr));
 }
 
 static ssize_t
@@ -535,7 +539,8 @@ spufs_fpcr_read(struct file *file, char __user * buffer,
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
-       ret = __spufs_fpcr_read(ctx, buffer, size, pos);
+       ret = simple_read_from_buffer(buffer, size, pos, &ctx->csa.lscsa->fpcr,
+                                     sizeof(ctx->csa.lscsa->fpcr));
        spu_release_saved(ctx);
        return ret;
 }
@@ -590,17 +595,12 @@ static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 mbox_data, __user *udata;
+       u32 mbox_data, __user *udata = (void __user *)buf;
        ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
-       if (!access_ok(buf, len))
-               return -EFAULT;
-
-       udata = (void __user *)buf;
-
        count = spu_acquire(ctx);
        if (count)
                return count;
@@ -616,7 +616,7 @@ static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
                 * but still need to return the data we have
                 * read successfully so far.
                 */
-               ret = __put_user(mbox_data, udata);
+               ret = put_user(mbox_data, udata);
                if (ret) {
                        if (!count)
                                count = -EFAULT;
@@ -698,17 +698,12 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 ibox_data, __user *udata;
+       u32 ibox_data, __user *udata = (void __user *)buf;
        ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
-       if (!access_ok(buf, len))
-               return -EFAULT;
-
-       udata = (void __user *)buf;
-
        count = spu_acquire(ctx);
        if (count)
                goto out;
@@ -727,7 +722,7 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
        }
 
        /* if we can't write at all, return -EFAULT */
-       count = __put_user(ibox_data, udata);
+       count = put_user(ibox_data, udata);
        if (count)
                goto out_unlock;
 
@@ -741,7 +736,7 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
                 * but still need to return the data we have
                 * read successfully so far.
                 */
-               ret = __put_user(ibox_data, udata);
+               ret = put_user(ibox_data, udata);
                if (ret)
                        break;
        }
@@ -836,17 +831,13 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
                        size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       u32 wbox_data, __user *udata;
+       u32 wbox_data, __user *udata = (void __user *)buf;
        ssize_t count;
 
        if (len < 4)
                return -EINVAL;
 
-       udata = (void __user *)buf;
-       if (!access_ok(buf, len))
-               return -EFAULT;
-
-       if (__get_user(wbox_data, udata))
+       if (get_user(wbox_data, udata))
                return -EFAULT;
 
        count = spu_acquire(ctx);
@@ -873,7 +864,7 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
        /* write as much as possible */
        for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
                int ret;
-               ret = __get_user(wbox_data, udata);
+               ret = get_user(wbox_data, udata);
                if (ret)
                        break;
 
@@ -967,28 +958,26 @@ spufs_signal1_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
-                       size_t len, loff_t *pos)
+static ssize_t spufs_signal1_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
 {
-       int ret = 0;
-       u32 data;
+       if (!ctx->csa.spu_chnlcnt_RW[3])
+               return 0;
+       return spufs_dump_emit(cprm, &ctx->csa.spu_chnldata_RW[3],
+                              sizeof(ctx->csa.spu_chnldata_RW[3]));
+}
 
-       if (len < 4)
+static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
+                       size_t len)
+{
+       if (len < sizeof(ctx->csa.spu_chnldata_RW[3]))
                return -EINVAL;
-
-       if (ctx->csa.spu_chnlcnt_RW[3]) {
-               data = ctx->csa.spu_chnldata_RW[3];
-               ret = 4;
-       }
-
-       if (!ret)
-               goto out;
-
-       if (copy_to_user(buf, &data, 4))
+       if (!ctx->csa.spu_chnlcnt_RW[3])
+               return 0;
+       if (copy_to_user(buf, &ctx->csa.spu_chnldata_RW[3],
+                        sizeof(ctx->csa.spu_chnldata_RW[3])))
                return -EFAULT;
-
-out:
-       return ret;
+       return sizeof(ctx->csa.spu_chnldata_RW[3]);
 }
 
 static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
@@ -1000,7 +989,7 @@ static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
-       ret = __spufs_signal1_read(ctx, buf, len, pos);
+       ret = __spufs_signal1_read(ctx, buf, len);
        spu_release_saved(ctx);
 
        return ret;
@@ -1104,28 +1093,26 @@ spufs_signal2_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
-                       size_t len, loff_t *pos)
+static ssize_t spufs_signal2_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
 {
-       int ret = 0;
-       u32 data;
+       if (!ctx->csa.spu_chnlcnt_RW[4])
+               return 0;
+       return spufs_dump_emit(cprm, &ctx->csa.spu_chnldata_RW[4],
+                              sizeof(ctx->csa.spu_chnldata_RW[4]));
+}
 
-       if (len < 4)
+static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
+                       size_t len)
+{
+       if (len < sizeof(ctx->csa.spu_chnldata_RW[4]))
                return -EINVAL;
-
-       if (ctx->csa.spu_chnlcnt_RW[4]) {
-               data =  ctx->csa.spu_chnldata_RW[4];
-               ret = 4;
-       }
-
-       if (!ret)
-               goto out;
-
-       if (copy_to_user(buf, &data, 4))
+       if (!ctx->csa.spu_chnlcnt_RW[4])
+               return 0;
+       if (copy_to_user(buf, &ctx->csa.spu_chnldata_RW[4],
+                        sizeof(ctx->csa.spu_chnldata_RW[4])))
                return -EFAULT;
-
-out:
-       return ret;
+       return sizeof(ctx->csa.spu_chnldata_RW[4]);
 }
 
 static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
@@ -1137,7 +1124,7 @@ static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
-       ret = __spufs_signal2_read(ctx, buf, len, pos);
+       ret = __spufs_signal2_read(ctx, buf, len);
        spu_release_saved(ctx);
 
        return ret;
@@ -1961,38 +1948,36 @@ static const struct file_operations spufs_caps_fops = {
        .release        = single_release,
 };
 
-static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
-                       char __user *buf, size_t len, loff_t *pos)
+static ssize_t spufs_mbox_info_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
 {
-       u32 data;
-
-       /* EOF if there's no entry in the mbox */
        if (!(ctx->csa.prob.mb_stat_R & 0x0000ff))
                return 0;
-
-       data = ctx->csa.prob.pu_mb_R;
-
-       return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
+       return spufs_dump_emit(cprm, &ctx->csa.prob.pu_mb_R,
+                              sizeof(ctx->csa.prob.pu_mb_R));
 }
 
 static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
-       int ret;
        struct spu_context *ctx = file->private_data;
-
-       if (!access_ok(buf, len))
-               return -EFAULT;
+       u32 stat, data;
+       int ret;
 
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_mbox_info_read(ctx, buf, len, pos);
+       stat = ctx->csa.prob.mb_stat_R;
+       data = ctx->csa.prob.pu_mb_R;
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       /* EOF if there's no entry in the mbox */
+       if (!(stat & 0x0000ff))
+               return 0;
+
+       return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
 }
 
 static const struct file_operations spufs_mbox_info_fops = {
@@ -2001,38 +1986,36 @@ static const struct file_operations spufs_mbox_info_fops = {
        .llseek  = generic_file_llseek,
 };
 
-static ssize_t __spufs_ibox_info_read(struct spu_context *ctx,
-                               char __user *buf, size_t len, loff_t *pos)
+static ssize_t spufs_ibox_info_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
 {
-       u32 data;
-
-       /* EOF if there's no entry in the ibox */
        if (!(ctx->csa.prob.mb_stat_R & 0xff0000))
                return 0;
-
-       data = ctx->csa.priv2.puint_mb_R;
-
-       return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
+       return spufs_dump_emit(cprm, &ctx->csa.priv2.puint_mb_R,
+                              sizeof(ctx->csa.priv2.puint_mb_R));
 }
 
 static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       u32 stat, data;
        int ret;
 
-       if (!access_ok(buf, len))
-               return -EFAULT;
-
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_ibox_info_read(ctx, buf, len, pos);
+       stat = ctx->csa.prob.mb_stat_R;
+       data = ctx->csa.priv2.puint_mb_R;
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       /* EOF if there's no entry in the ibox */
+       if (!(stat & 0xff0000))
+               return 0;
+
+       return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
 }
 
 static const struct file_operations spufs_ibox_info_fops = {
@@ -2041,41 +2024,36 @@ static const struct file_operations spufs_ibox_info_fops = {
        .llseek  = generic_file_llseek,
 };
 
-static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
-                       char __user *buf, size_t len, loff_t *pos)
+static size_t spufs_wbox_info_cnt(struct spu_context *ctx)
 {
-       int i, cnt;
-       u32 data[4];
-       u32 wbox_stat;
-
-       wbox_stat = ctx->csa.prob.mb_stat_R;
-       cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
-       for (i = 0; i < cnt; i++) {
-               data[i] = ctx->csa.spu_mailbox_data[i];
-       }
+       return (4 - ((ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8)) * sizeof(u32);
+}
 
-       return simple_read_from_buffer(buf, len, pos, &data,
-                               cnt * sizeof(u32));
+static ssize_t spufs_wbox_info_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
+{
+       return spufs_dump_emit(cprm, &ctx->csa.spu_mailbox_data,
+                       spufs_wbox_info_cnt(ctx));
 }
 
 static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
-       int ret;
-
-       if (!access_ok(buf, len))
-               return -EFAULT;
+       u32 data[ARRAY_SIZE(ctx->csa.spu_mailbox_data)];
+       int ret, count;
 
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_wbox_info_read(ctx, buf, len, pos);
+       count = spufs_wbox_info_cnt(ctx);
+       memcpy(&data, &ctx->csa.spu_mailbox_data, sizeof(data));
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &data,
+                               count * sizeof(u32));
 }
 
 static const struct file_operations spufs_wbox_info_fops = {
@@ -2084,50 +2062,53 @@ static const struct file_operations spufs_wbox_info_fops = {
        .llseek  = generic_file_llseek,
 };
 
-static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
-                       char __user *buf, size_t len, loff_t *pos)
+static void spufs_get_dma_info(struct spu_context *ctx,
+               struct spu_dma_info *info)
 {
-       struct spu_dma_info info;
-       struct mfc_cq_sr *qp, *spuqp;
        int i;
 
-       info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
-       info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
-       info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
-       info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
-       info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
+       info->dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
+       info->dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
+       info->dma_info_status = ctx->csa.spu_chnldata_RW[24];
+       info->dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
+       info->dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
        for (i = 0; i < 16; i++) {
-               qp = &info.dma_info_command_data[i];
-               spuqp = &ctx->csa.priv2.spuq[i];
+               struct mfc_cq_sr *qp = &info->dma_info_command_data[i];
+               struct mfc_cq_sr *spuqp = &ctx->csa.priv2.spuq[i];
 
                qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
                qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
                qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
                qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
        }
+}
 
-       return simple_read_from_buffer(buf, len, pos, &info,
-                               sizeof info);
+static ssize_t spufs_dma_info_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
+{
+       struct spu_dma_info info;
+
+       spufs_get_dma_info(ctx, &info);
+       return spufs_dump_emit(cprm, &info, sizeof(info));
 }
 
 static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
                              size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       struct spu_dma_info info;
        int ret;
 
-       if (!access_ok(buf, len))
-               return -EFAULT;
-
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_dma_info_read(ctx, buf, len, pos);
+       spufs_get_dma_info(ctx, &info);
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &info,
+                               sizeof(info));
 }
 
 static const struct file_operations spufs_dma_info_fops = {
@@ -2136,52 +2117,55 @@ static const struct file_operations spufs_dma_info_fops = {
        .llseek = no_llseek,
 };
 
-static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
-                       char __user *buf, size_t len, loff_t *pos)
+static void spufs_get_proxydma_info(struct spu_context *ctx,
+               struct spu_proxydma_info *info)
 {
-       struct spu_proxydma_info info;
-       struct mfc_cq_sr *qp, *puqp;
-       int ret = sizeof info;
        int i;
 
-       if (len < ret)
-               return -EINVAL;
-
-       if (!access_ok(buf, len))
-               return -EFAULT;
+       info->proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
+       info->proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
+       info->proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
 
-       info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
-       info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
-       info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
        for (i = 0; i < 8; i++) {
-               qp = &info.proxydma_info_command_data[i];
-               puqp = &ctx->csa.priv2.puq[i];
+               struct mfc_cq_sr *qp = &info->proxydma_info_command_data[i];
+               struct mfc_cq_sr *puqp = &ctx->csa.priv2.puq[i];
 
                qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
                qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
                qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
                qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
        }
+}
 
-       return simple_read_from_buffer(buf, len, pos, &info,
-                               sizeof info);
+static ssize_t spufs_proxydma_info_dump(struct spu_context *ctx,
+               struct coredump_params *cprm)
+{
+       struct spu_proxydma_info info;
+
+       spufs_get_proxydma_info(ctx, &info);
+       return spufs_dump_emit(cprm, &info, sizeof(info));
 }
 
 static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
                                   size_t len, loff_t *pos)
 {
        struct spu_context *ctx = file->private_data;
+       struct spu_proxydma_info info;
        int ret;
 
+       if (len < sizeof(info))
+               return -EINVAL;
+
        ret = spu_acquire_saved(ctx);
        if (ret)
                return ret;
        spin_lock(&ctx->csa.register_lock);
-       ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
+       spufs_get_proxydma_info(ctx, &info);
        spin_unlock(&ctx->csa.register_lock);
        spu_release_saved(ctx);
 
-       return ret;
+       return simple_read_from_buffer(buf, len, pos, &info,
+                               sizeof(info));
 }
 
 static const struct file_operations spufs_proxydma_info_fops = {
@@ -2625,23 +2609,23 @@ const struct spufs_tree_descr spufs_dir_debug_contents[] = {
 };
 
 const struct spufs_coredump_reader spufs_coredump_read[] = {
-       { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])},
-       { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) },
+       { "regs", spufs_regs_dump, NULL, sizeof(struct spu_reg128[128])},
+       { "fpcr", spufs_fpcr_dump, NULL, sizeof(struct spu_reg128) },
        { "lslr", NULL, spufs_lslr_get, 19 },
        { "decr", NULL, spufs_decr_get, 19 },
        { "decr_status", NULL, spufs_decr_status_get, 19 },
-       { "mem", __spufs_mem_read, NULL, LS_SIZE, },
-       { "signal1", __spufs_signal1_read, NULL, sizeof(u32) },
+       { "mem", spufs_mem_dump, NULL, LS_SIZE, },
+       { "signal1", spufs_signal1_dump, NULL, sizeof(u32) },
        { "signal1_type", NULL, spufs_signal1_type_get, 19 },
-       { "signal2", __spufs_signal2_read, NULL, sizeof(u32) },
+       { "signal2", spufs_signal2_dump, NULL, sizeof(u32) },
        { "signal2_type", NULL, spufs_signal2_type_get, 19 },
        { "event_mask", NULL, spufs_event_mask_get, 19 },
        { "event_status", NULL, spufs_event_status_get, 19 },
-       { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) },
-       { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) },
-       { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)},
-       { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)},
-       { "proxydma_info", __spufs_proxydma_info_read,
+       { "mbox_info", spufs_mbox_info_dump, NULL, sizeof(u32) },
+       { "ibox_info", spufs_ibox_info_dump, NULL, sizeof(u32) },
+       { "wbox_info", spufs_wbox_info_dump, NULL, 4 * sizeof(u32)},
+       { "dma_info", spufs_dma_info_dump, NULL, sizeof(struct spu_dma_info)},
+       { "proxydma_info", spufs_proxydma_info_dump,
                           NULL, sizeof(struct spu_proxydma_info)},
        { "object-id", NULL, spufs_object_id_get, 19 },
        { "npc", NULL, spufs_npc_get, 19 },
index 413c89afe1126e5d24c37d766008002e9ac4a8ba..1ba4d884febfa0c085a07d7f3d56df8ad9168a53 100644 (file)
@@ -337,8 +337,7 @@ void spufs_dma_callback(struct spu *spu, int type);
 extern struct spu_coredump_calls spufs_coredump_calls;
 struct spufs_coredump_reader {
        char *name;
-       ssize_t (*read)(struct spu_context *ctx,
-                       char __user *buffer, size_t size, loff_t *pos);
+       ssize_t (*dump)(struct spu_context *ctx, struct coredump_params *cprm);
        u64 (*get)(struct spu_context *ctx);
        size_t size;
 };
index 35b60035b6b0640313938df1cd871ebd3ea004ac..d50706ea1c943ba7dbbb39ba7a2e8ec047b71bff 100644 (file)
@@ -473,9 +473,9 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
 #define PAGE_SHARED            __pgprot(0)
 #define PAGE_KERNEL            __pgprot(0)
 #define swapper_pg_dir         NULL
+#define TASK_SIZE              0xffffffffUL
 #define VMALLOC_START          0
-
-#define TASK_SIZE 0xffffffffUL
+#define VMALLOC_END            TASK_SIZE
 
 static inline void __kernel_map_pages(struct page *page, int numpages, int enable) {}
 
index 7eab76a931061010c0b16bbc6da458827658f94f..070505d79b065986aa6904da71b594cdab0c9cbd 100644 (file)
@@ -204,7 +204,7 @@ static void note_prot_wx(struct pg_state *st, unsigned long addr)
 }
 
 static void note_page(struct ptdump_state *pt_st, unsigned long addr,
-                     int level, unsigned long val)
+                     int level, u64 val)
 {
        struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
        u64 pa = PFN_PHYS(pte_pfn(__pte(val)));
index 7c15542d3685479713a06643e883503789d7b7a5..698b1e6d3c14d1c3a2b8176030508d0bb7f2af91 100644 (file)
@@ -27,7 +27,7 @@
 
 #include "sha.h"
 
-static int sha1_init(struct shash_desc *desc)
+static int s390_sha1_init(struct shash_desc *desc)
 {
        struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
 
@@ -42,7 +42,7 @@ static int sha1_init(struct shash_desc *desc)
        return 0;
 }
 
-static int sha1_export(struct shash_desc *desc, void *out)
+static int s390_sha1_export(struct shash_desc *desc, void *out)
 {
        struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
        struct sha1_state *octx = out;
@@ -53,7 +53,7 @@ static int sha1_export(struct shash_desc *desc, void *out)
        return 0;
 }
 
-static int sha1_import(struct shash_desc *desc, const void *in)
+static int s390_sha1_import(struct shash_desc *desc, const void *in)
 {
        struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
        const struct sha1_state *ictx = in;
@@ -67,11 +67,11 @@ static int sha1_import(struct shash_desc *desc, const void *in)
 
 static struct shash_alg alg = {
        .digestsize     =       SHA1_DIGEST_SIZE,
-       .init           =       sha1_init,
+       .init           =       s390_sha1_init,
        .update         =       s390_sha_update,
        .final          =       s390_sha_final,
-       .export         =       sha1_export,
-       .import         =       sha1_import,
+       .export         =       s390_sha1_export,
+       .import         =       s390_sha1_import,
        .descsize       =       sizeof(struct s390_sha_ctx),
        .statesize      =       sizeof(struct sha1_state),
        .base           =       {
index 91e376b0d28c25edfbbd5aa8d7f4305f43387e7f..6d01c96aeb5c4e0eb47e8edd4b950e37770ece08 100644 (file)
@@ -39,25 +39,6 @@ csum_partial(const void *buff, int len, __wsum sum)
        return sum;
 }
 
-/*
- * the same as csum_partial_copy, but copies from user space.
- *
- * here even more important to align src and dst on a 32-bit (or even
- * better 64-bit) boundary
- *
- * Copy from userspace and compute checksum.
- */
-static inline __wsum
-csum_partial_copy_from_user(const void __user *src, void *dst,
-                                          int len, __wsum sum,
-                                          int *err_ptr)
-{
-       if (unlikely(copy_from_user(dst, src, len)))
-               *err_ptr = -EFAULT;
-       return csum_partial(dst, len, sum);
-}
-
-
 static inline __wsum
 csum_partial_copy_nocheck (const void *src, void *dst, int len, __wsum sum)
 {
index 36445dd40fdbd68bd2b8b1a8b793797d45eeb5f9..0f0b140b5558172836ddf3c70ac92485c7f96afb 100644 (file)
@@ -305,12 +305,9 @@ void *restart_stack __section(.data);
 unsigned long stack_alloc(void)
 {
 #ifdef CONFIG_VMAP_STACK
-       return (unsigned long)
-               __vmalloc_node_range(THREAD_SIZE, THREAD_SIZE,
-                                    VMALLOC_START, VMALLOC_END,
-                                    THREADINFO_GFP,
-                                    PAGE_KERNEL, 0, NUMA_NO_NODE,
-                                    __builtin_return_address(0));
+       return (unsigned long)__vmalloc_node(THREAD_SIZE, THREAD_SIZE,
+                       THREADINFO_GFP, NUMA_NO_NODE,
+                       __builtin_return_address(0));
 #else
        return __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
 #endif
index bd7bd3581a0fcd4f830d774c8fb98b53fa0066db..bfdcb7633957355c4fa5b4d397f52e13534a5141 100644 (file)
 435  common    clone3                  sys_clone3                      sys_clone3
 437  common    openat2                 sys_openat2                     sys_openat2
 438  common    pidfd_getfd             sys_pidfd_getfd                 sys_pidfd_getfd
+439  common    faccessat2              sys_faccessat2                  sys_faccessat2
index b4f0e37b83eb548b8937ce06cb9c7a2c7fd47e98..97656d20b9eac275458a71a4c16d944d984cd0e3 100644 (file)
@@ -71,7 +71,6 @@ config SUPERH32
        select HAVE_FUNCTION_TRACER
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_DYNAMIC_FTRACE
-       select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
        select ARCH_WANT_IPC_PARSE_VERSION
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_ARCH_KGDB
index 36b84cfd3f673f25722eddaf7a4b5cbd397dc9d3..91571a42e44ed6aaa24d5c7670414fa02b2f45d1 100644 (file)
@@ -48,12 +48,17 @@ __wsum csum_partial_copy_nocheck(const void *src, void *dst,
        return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
 }
 
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
 static inline
-__wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+__wsum csum_and_copy_from_user(const void __user *src, void *dst,
                                   int len, __wsum sum, int *err_ptr)
 {
-       return csum_partial_copy_generic((__force const void *)src, dst,
+       if (access_ok(src, len))
+               return csum_partial_copy_generic((__force const void *)src, dst,
                                        len, sum, err_ptr, NULL);
+       if (len)
+               *err_ptr = -EFAULT;
+       return sum;
 }
 
 /*
index 934ff84844fa0bb8819d89fb7a13cf6ee00aa9a3..d432164b23b7cef2f6766ced504754e695ac2518 100644 (file)
@@ -103,7 +103,8 @@ static int __sq_remap(struct sq_mapping *map, pgprot_t prot)
 #if defined(CONFIG_MMU)
        struct vm_struct *vma;
 
-       vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX);
+       vma = __get_vm_area_caller(map->size, VM_ALLOC, map->sq_addr,
+                       SQ_ADDRMAX, __builtin_return_address(0));
        if (!vma)
                return -ENOMEM;
 
index c7a30fcd135f89cc4882ed67e8c6bf5a4ea54347..acc35daa1b7924eb697cd822892d757174e51d90 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 63cf17bc760da5143e571fb925ac76e558d403e2..2130381c9d5749ae2d22eadbc279ac84c92b60cf 100644 (file)
@@ -170,11 +170,21 @@ BUILD_TRAP_HANDLER(bug)
        force_sig(SIGTRAP);
 }
 
+#ifdef CONFIG_DYNAMIC_FTRACE
+extern void arch_ftrace_nmi_enter(void);
+extern void arch_ftrace_nmi_exit(void);
+#else
+static inline void arch_ftrace_nmi_enter(void) { }
+static inline void arch_ftrace_nmi_exit(void) { }
+#endif
+
 BUILD_TRAP_HANDLER(nmi)
 {
        unsigned int cpu = smp_processor_id();
        TRAP_HANDLER_DECL;
 
+       arch_ftrace_nmi_enter();
+
        nmi_enter();
        nmi_count(cpu)++;
 
@@ -190,4 +200,6 @@ BUILD_TRAP_HANDLER(nmi)
        }
 
        nmi_exit();
+
+       arch_ftrace_nmi_exit();
 }
index 14f6c15be6aecda043fb1733a32cd9922dc0c1cf..111283fe837e8d3fcc598757496910f91cb9390a 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/md5.h>
 
index 7c16663044417c9bd5fa01b0484a86fa90a1bde6..dc017782be523d000aac2d10e0ace71584fbeaaa 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 
index f403ce9ba6e4f37586819550e5080227f3e71d24..286bc8ecf15b6ffbb8ed7cbb32e60d8d0da33f00 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 
index a3b532e43c074e2f426f672949602f9ae3daa725..3b2ca732ff7a5a3b63d5a15c5afd97209d3a40e2 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 
index c3be56e2e768a775456b3defab331380f1324a8c..a6256cb6fc5cc451ce54ba4944416c7d034eacda 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #ifndef ___ASM_SPARC_CHECKSUM_H
 #define ___ASM_SPARC_CHECKSUM_H
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
 #if defined(__sparc__) && defined(__arch64__)
 #include <asm/checksum_64.h>
 #else
index 5fc98d80b03bccd0a3be9aea757836bd0465535e..479a0b812af50a59ada387e54fc1f697cfe068ff 100644 (file)
@@ -60,7 +60,7 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
 }
 
 static inline __wsum
-csum_partial_copy_from_user(const void __user *src, void *dst, int len,
+csum_and_copy_from_user(const void __user *src, void *dst, int len,
                            __wsum sum, int *err)
   {
        register unsigned long ret asm("o0") = (unsigned long)src;
@@ -68,6 +68,12 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
        register int l asm("g1") = len;
        register __wsum s asm("g7") = sum;
 
+       if (unlikely(!access_ok(src, len))) {
+               if (len)
+                       *err = -EFAULT;
+               return sum;
+       }
+
        __asm__ __volatile__ (
        ".section __ex_table,#alloc\n\t"
        ".align 4\n\t"
@@ -83,8 +89,10 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
        return (__force __wsum)ret;
 }
 
+#define HAVE_CSUM_COPY_USER
+
 static inline __wsum
-csum_partial_copy_to_user(const void *src, void __user *dst, int len,
+csum_and_copy_to_user(const void *src, void __user *dst, int len,
                          __wsum sum, int *err)
 {
        if (!access_ok(dst, len)) {
@@ -113,9 +121,6 @@ csum_partial_copy_to_user(const void *src, void __user *dst, int len,
        }
 }
 
-#define HAVE_CSUM_COPY_USER
-#define csum_and_copy_to_user csum_partial_copy_to_user
-
 /* ihl is always 5 or greater, almost always is 5, and iph is word aligned
  * the majority of the time.
  */
index e52450930e4eacebb9c20cb3c4145320c7ddcc1b..0fa4433f5662ba77309957e0ae28ae7bc6b64a23 100644 (file)
@@ -46,7 +46,7 @@ long __csum_partial_copy_from_user(const void __user *src,
                                   __wsum sum);
 
 static inline __wsum
-csum_partial_copy_from_user(const void __user *src,
+csum_and_copy_from_user(const void __user *src,
                            void *dst, int len,
                            __wsum sum, int *err)
 {
index f13615ecdecce2f69adbd13576b03a6aa4d8fbef..8004a276cb74be3c2e2ff0f9053d884f3936cc61 100644 (file)
 # 435 reserved for clone3
 437    common  openat2                 sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index f4b84872d6403471c5bed0e61235957ebada5aa8..7314450089320af7c870746757860b5eb951fd16 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/cryptohash.h>
 #include <linux/delay.h>
 #include <linux/in6.h>
 #include <linux/syscalls.h>
index 2d3f963fd6f13e14c943d9e732abded33e532c3f..e5d38cd11df0291f43039c61b9ec3f9e46124edb 100644 (file)
@@ -92,6 +92,7 @@ config X86
        select ARCH_USE_BUILTIN_BSWAP
        select ARCH_USE_QUEUED_RWLOCKS
        select ARCH_USE_QUEUED_SPINLOCKS
+       select ARCH_USE_SYM_ANNOTATIONS
        select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
        select ARCH_WANT_DEFAULT_BPF_JIT        if X86_64
        select ARCH_WANTS_DYNAMIC_TASK_STRUCT
@@ -596,7 +597,7 @@ config X86_INTEL_MID
        select I2C
        select DW_APB_TIMER
        select APB_TIMER
-       select INTEL_SCU_IPC
+       select INTEL_SCU_PCI
        select MFD_INTEL_MSIC
        ---help---
          Select to build a kernel capable of supporting Intel MID (Mobile
@@ -1611,19 +1612,10 @@ config NODES_SHIFT
          Specify the maximum number of NUMA Nodes available on the target
          system.  Increases memory reserved to accommodate various tables.
 
-config ARCH_HAVE_MEMORY_PRESENT
-       def_bool y
-       depends on X86_32 && DISCONTIGMEM
-
 config ARCH_FLATMEM_ENABLE
        def_bool y
        depends on X86_32 && !NUMA
 
-config ARCH_DISCONTIGMEM_ENABLE
-       def_bool n
-       depends on NUMA && X86_32
-       depends on BROKEN
-
 config ARCH_SPARSEMEM_ENABLE
        def_bool y
        depends on X86_64 || NUMA || X86_32 || X86_32_NON_STANDARD
@@ -1888,10 +1880,10 @@ config X86_UMIP
          results are dummy.
 
 config X86_INTEL_MEMORY_PROTECTION_KEYS
-       prompt "Intel Memory Protection Keys"
+       prompt "Memory Protection Keys"
        def_bool y
        # Note: only available in 64-bit mode
-       depends on CPU_SUP_INTEL && X86_64
+       depends on X86_64 && (CPU_SUP_INTEL || CPU_SUP_AMD)
        select ARCH_USES_HIGH_VMA_FLAGS
        select ARCH_HAS_PKEYS
        ---help---
index 2e74690b028a5b93306f689b9cd9ef8f82649e5c..f909d3ce36e6e289d8077715b18cc63de9a45f56 100644 (file)
@@ -99,15 +99,6 @@ config DEBUG_WX
 
          If in doubt, say "Y".
 
-config DOUBLEFAULT
-       default y
-       bool "Enable doublefault exception handler" if EXPERT && X86_32
-       ---help---
-         This option allows trapping of rare doublefault exceptions that
-         would otherwise cause a system to silently reboot. Disabling this
-         option saves about 4k and might cause you much additional grey
-         hair.
-
 config DEBUG_TLBFLUSH
        bool "Set upper limit of TLB entries to flush one-by-one"
        depends on DEBUG_KERNEL
index b65ec63c7db7b2ec45e02bc32f08e037f55cc41f..00e378de8bc0b3b9feabe378845f966ce2ff1a74 100644 (file)
@@ -246,7 +246,7 @@ drivers-$(CONFIG_FB) += arch/x86/video/
 
 boot := arch/x86/boot
 
-BOOT_TARGETS = bzlilo bzdisk fdimage fdimage144 fdimage288 isoimage
+BOOT_TARGETS = bzdisk fdimage fdimage144 fdimage288 isoimage
 
 PHONY += bzImage $(BOOT_TARGETS)
 
@@ -267,8 +267,8 @@ endif
 $(BOOT_TARGETS): vmlinux
        $(Q)$(MAKE) $(build)=$(boot) $@
 
-PHONY += install
-install:
+PHONY += install bzlilo
+install bzlilo:
        $(Q)$(MAKE) $(build)=$(boot) $@
 
 PHONY += vdso_install
index e17be90ab3129cda2d83047b644c31838048c5b6..4c53556843212fdf02d8fb9ab5bd8ff4b53c5251 100644 (file)
@@ -57,11 +57,10 @@ $(obj)/cpu.o: $(obj)/cpustr.h
 
 quiet_cmd_cpustr = CPUSTR  $@
       cmd_cpustr = $(obj)/mkcpustr > $@
-targets += cpustr.h
 $(obj)/cpustr.h: $(obj)/mkcpustr FORCE
        $(call if_changed,cpustr)
 endif
-clean-files += cpustr.h
+targets += cpustr.h
 
 # ---------------------------------------------------------------------------
 
@@ -129,6 +128,8 @@ quiet_cmd_genimage = GENIMAGE $3
 cmd_genimage = sh $(srctree)/$(src)/genimage.sh $2 $3 $(obj)/bzImage \
                        $(obj)/mtools.conf '$(image_cmdline)' $(FDINITRD)
 
+PHONY += bzdisk fdimage fdimage144 fdimage288 isoimage bzlilo install
+
 # This requires write access to /dev/fd0
 bzdisk: $(obj)/bzImage $(obj)/mtools.conf
        $(call cmd,genimage,bzdisk,/dev/fd0)
@@ -146,7 +147,7 @@ isoimage: $(obj)/bzImage
        $(call cmd,genimage,isoimage,$(obj)/image.iso)
        @$(kecho) 'Kernel: $(obj)/image.iso is ready'
 
-bzlilo: $(obj)/bzImage
+bzlilo:
        if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
        if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
        cat $(obj)/bzImage > $(INSTALL_PATH)/vmlinuz
index ef2ad7253cd5e96634c8d4164e5c8df3f2e06ed1..8bcbcee54aa13703f82bc7e48c02bfdb48351e76 100644 (file)
@@ -280,9 +280,9 @@ acpi_physical_address get_rsdp_addr(void)
  */
 #define MAX_ADDR_LEN 19
 
-static acpi_physical_address get_cmdline_acpi_rsdp(void)
+static unsigned long get_cmdline_acpi_rsdp(void)
 {
-       acpi_physical_address addr = 0;
+       unsigned long addr = 0;
 
 #ifdef CONFIG_KEXEC
        char val[MAX_ADDR_LEN] = { };
@@ -292,7 +292,7 @@ static acpi_physical_address get_cmdline_acpi_rsdp(void)
        if (ret < 0)
                return 0;
 
-       if (kstrtoull(val, 16, &addr))
+       if (boot_kstrtoul(val, 16, &addr))
                return 0;
 #endif
        return addr;
@@ -314,7 +314,6 @@ static unsigned long get_acpi_srat_table(void)
         * different ideas about whether to trust a command-line parameter.
         */
        rsdp = (struct acpi_table_rsdp *)get_cmdline_acpi_rsdp();
-
        if (!rsdp)
                rsdp = (struct acpi_table_rsdp *)(long)
                        boot_params->acpi_rsdp_addr;
index 2b2049259619f1ec3aa52a787f46251d0ee76ec7..c4bb0f9363f5ee6494f86780d2a93ff82cc40fb3 100644 (file)
@@ -28,8 +28,6 @@ SYM_FUNC_START(__efi64_thunk)
        push    %rbx
 
        leaq    1f(%rip), %rbp
-       leaq    efi_gdt64(%rip), %rbx
-       movl    %ebx, 2(%rbx)           /* Fixup the gdt base address */
 
        movl    %ds, %eax
        push    %rax
@@ -48,7 +46,8 @@ SYM_FUNC_START(__efi64_thunk)
        movl    %r8d, 0xc(%rsp)
        movl    %r9d, 0x10(%rsp)
 
-       sgdt    0x14(%rsp)
+       leaq    0x14(%rsp), %rbx
+       sgdt    (%rbx)
 
        /*
         * Switch to gdt with 32-bit segments. This is the firmware GDT
@@ -68,8 +67,7 @@ SYM_FUNC_START(__efi64_thunk)
        pushq   %rax
        lretq
 
-1:     lgdt    0x14(%rsp)
-       addq    $32, %rsp
+1:     addq    $32, %rsp
        movq    %rdi, %rax
 
        pop     %rbx
@@ -175,14 +173,3 @@ SYM_DATA_END(efi32_boot_cs)
 SYM_DATA_START(efi32_boot_ds)
        .word   0
 SYM_DATA_END(efi32_boot_ds)
-
-SYM_DATA_START(efi_gdt64)
-       .word   efi_gdt64_end - efi_gdt64
-       .long   0                       /* Filled out by user */
-       .word   0
-       .quad   0x0000000000000000      /* NULL descriptor */
-       .quad   0x00af9a000000ffff      /* __KERNEL_CS */
-       .quad   0x00cf92000000ffff      /* __KERNEL_DS */
-       .quad   0x0080890000000000      /* TS descriptor */
-       .quad   0x0000000000000000      /* TS continued */
-SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)
index ab3307036ba44392911c7452a5c1d088fdc01997..03557f2174bfa5d6c5f92232e6a56632160ed05c 100644 (file)
  * Position Independent Executable (PIE) so that linker won't optimize
  * R_386_GOT32X relocation to its fixed symbol address.  Older
  * linkers generate R_386_32 relocations against locally defined symbols,
- * _bss, _ebss, _got and _egot, in PIE.  It isn't wrong, just less
+ * _bss, _ebss, _got, _egot and _end, in PIE.  It isn't wrong, just less
  * optimal than R_386_RELATIVE.  But the x86 kernel fails to properly handle
  * R_386_32 relocations when relocating the kernel.  To generate
- * R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as
+ * R_386_RELATIVE relocations, we mark _bss, _ebss, _got, _egot and _end as
  * hidden:
  */
        .hidden _bss
        .hidden _ebss
        .hidden _got
        .hidden _egot
+       .hidden _end
 
        __HEAD
 SYM_FUNC_START(startup_32)
index 4f7e6b84be0703a62849ce1c3d1ecd9c6a29a325..e821a7d7d5c4f8e5fa101c0e24961f2de1f11eeb 100644 (file)
@@ -42,6 +42,7 @@
        .hidden _ebss
        .hidden _got
        .hidden _egot
+       .hidden _end
 
        __HEAD
        .code32
@@ -393,6 +394,14 @@ SYM_CODE_START(startup_64)
        addq    %rax, 2(%rax)
        lgdt    (%rax)
 
+       /* Reload CS so IRET returns to a CS actually in the GDT */
+       pushq   $__KERNEL_CS
+       leaq    .Lon_kernel_cs(%rip), %rax
+       pushq   %rax
+       lretq
+
+.Lon_kernel_cs:
+
        /*
         * paging_prepare() sets up the trampoline and checks if we need to
         * enable 5-level paging.
index 508cfa6828c5d88cd8f616e28df686ec625dd5f5..8f1025d1f6810a841a4d034e900bf25d98dc3f9e 100644 (file)
@@ -52,6 +52,7 @@ SECTIONS
                _data = . ;
                *(.data)
                *(.data.*)
+               *(.bss.efistub)
                _edata = . ;
        }
        . = ALIGN(L1_CACHE_BYTES);
@@ -73,4 +74,6 @@ SECTIONS
 #endif
        . = ALIGN(PAGE_SIZE);   /* keep ZO size page aligned */
        _end = .;
+
+       DISCARDS
 }
index 8272a44928444ec6a679819db52c6e1adefc5f80..8a3fff9128bb4a312c5c7cf8a8b8caa488a8e506 100644 (file)
@@ -117,7 +117,6 @@ static unsigned int simple_guess_base(const char *cp)
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  */
-
 unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
 {
        unsigned long long result = 0;
@@ -335,3 +334,45 @@ int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
                s++;
        return _kstrtoull(s, base, res);
 }
+
+static int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       rv = kstrtoull(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (unsigned long)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+
+/**
+ * kstrtoul - convert a string to an unsigned long
+ * @s: The start of the string. The string must be null-terminated, and may also
+ *  include a single newline before its terminating null. The first character
+ *  may also be a plus sign, but not a minus sign.
+ * @base: The number base to use. The maximum supported base is 16. If base is
+ *  given as 0, then the base of the string is automatically detected with the
+ *  conventional semantics - If it begins with 0x the number will be parsed as a
+ *  hexadecimal (case insensitive), if it otherwise begins with 0, it will be
+ *  parsed as an octal number. Otherwise it will be parsed as a decimal.
+ * @res: Where to write the result of the conversion on success.
+ *
+ * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
+ * Used as a replacement for the simple_strtoull.
+ */
+int boot_kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+       /*
+        * We want to shortcut function call, but
+        * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0.
+        */
+       if (sizeof(unsigned long) == sizeof(unsigned long long) &&
+           __alignof__(unsigned long) == __alignof__(unsigned long long))
+               return kstrtoull(s, base, (unsigned long long *)res);
+       else
+               return _kstrtoul(s, base, res);
+}
index 38d8f2f5e47e2bf66ff9bb02a994f848dde511b8..995f7b7ad512ef3b3faba63dda8244e860a6cfe4 100644 (file)
@@ -30,4 +30,5 @@ extern unsigned long long simple_strtoull(const char *cp, char **endp,
                                          unsigned int base);
 
 int kstrtoull(const char *s, unsigned int base, unsigned long long *res);
+int boot_kstrtoul(const char *s, unsigned int base, unsigned long *res);
 #endif /* BOOT_STRING_H */
index cad6e1bfa7d5f237559655b2b12d3070300972c6..54e7d15dbd0d5af8af4ed3a27b5a509d54697f34 100644 (file)
@@ -2758,7 +2758,7 @@ SYM_FUNC_START(aesni_xts_crypt8)
        pxor INC, STATE4
        movdqu IV, 0x30(OUTP)
 
-       CALL_NOSPEC %r11
+       CALL_NOSPEC r11
 
        movdqu 0x00(OUTP), INC
        pxor INC, STATE1
@@ -2803,7 +2803,7 @@ SYM_FUNC_START(aesni_xts_crypt8)
        _aesni_gf128mul_x_ble()
        movups IV, (IVP)
 
-       CALL_NOSPEC %r11
+       CALL_NOSPEC r11
 
        movdqu 0x40(OUTP), INC
        pxor INC, STATE1
index d01ddd73de65dabda6c8ed781e2239f15cc17dc2..ecc0a9a905c481998e934781a76069917cf0d120 100644 (file)
@@ -1228,7 +1228,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_16way)
        vpxor 14 * 16(%rax), %xmm15, %xmm14;
        vpxor 15 * 16(%rax), %xmm15, %xmm15;
 
-       CALL_NOSPEC %r9;
+       CALL_NOSPEC r9;
 
        addq $(16 * 16), %rsp;
 
index 563ef6e83cdd213fcc80a5aa81f92b0f71c46071..0907243c501cdca38907d06601c696559e21358a 100644 (file)
@@ -1339,7 +1339,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_32way)
        vpxor 14 * 32(%rax), %ymm15, %ymm14;
        vpxor 15 * 32(%rax), %ymm15, %ymm15;
 
-       CALL_NOSPEC %r9;
+       CALL_NOSPEC r9;
 
        addq $(16 * 32), %rsp;
 
index 0e6690e3618c7103c20ce9b1d937e22616d2ff2c..8501ec4532f4f5966805cbb49853bd3e8af75a9d 100644 (file)
@@ -75,7 +75,7 @@
 
 .text
 SYM_FUNC_START(crc_pcl)
-#define    bufp                %rdi
+#define    bufp                rdi
 #define    bufp_dw     %edi
 #define    bufp_w      %di
 #define    bufp_b      %dil
@@ -105,9 +105,9 @@ SYM_FUNC_START(crc_pcl)
        ## 1) ALIGN:
        ################################################################
 
-       mov     bufp, bufptmp           # rdi = *buf
-       neg     bufp
-       and     $7, bufp                # calculate the unalignment amount of
+       mov     %bufp, bufptmp          # rdi = *buf
+       neg     %bufp
+       and     $7, %bufp               # calculate the unalignment amount of
                                        # the address
        je      proc_block              # Skip if aligned
 
@@ -123,13 +123,13 @@ SYM_FUNC_START(crc_pcl)
 do_align:
        #### Calculate CRC of unaligned bytes of the buffer (if any)
        movq    (bufptmp), tmp          # load a quadward from the buffer
-       add     bufp, bufptmp           # align buffer pointer for quadword
+       add     %bufp, bufptmp          # align buffer pointer for quadword
                                        # processing
-       sub     bufp, len               # update buffer length
+       sub     %bufp, len              # update buffer length
 align_loop:
        crc32b  %bl, crc_init_dw        # compute crc32 of 1-byte
        shr     $8, tmp                 # get next byte
-       dec     bufp
+       dec     %bufp
        jne     align_loop
 
 proc_block:
@@ -169,10 +169,10 @@ continue_block:
        xor     crc2, crc2
 
        ## branch into array
-       lea     jump_table(%rip), bufp
-       movzxw  (bufp, %rax, 2), len
-       lea     crc_array(%rip), bufp
-       lea     (bufp, len, 1), bufp
+       lea     jump_table(%rip), %bufp
+       movzxw  (%bufp, %rax, 2), len
+       lea     crc_array(%rip), %bufp
+       lea     (%bufp, len, 1), %bufp
        JMP_NOSPEC bufp
 
        ################################################################
@@ -218,9 +218,9 @@ LABEL crc_ %i
        ## 4) Combine three results:
        ################################################################
 
-       lea     (K_table-8)(%rip), bufp         # first entry is for idx 1
+       lea     (K_table-8)(%rip), %bufp                # first entry is for idx 1
        shlq    $3, %rax                        # rax *= 8
-       pmovzxdq (bufp,%rax), %xmm0             # 2 consts: K1:K2
+       pmovzxdq (%bufp,%rax), %xmm0            # 2 consts: K1:K2
        leal    (%eax,%eax,2), %eax             # rax *= 3 (total *24)
        subq    %rax, tmp                       # tmp -= rax*24
 
index a801ffc10cbbf71263ec359c88fcbdfc6036c82e..18200135603fc9598075b7aed5d8e3069c149138 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha1_base.h>
index 6394b5fe8db6da51f8c58c8a59682c154ab9f2dc..dd06249229e169629ba65411a99ca2d488b72686 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha256_base.h>
index 82cc1b3ced1dbe6df5569d7065b0fda2ff1df9bf..b0b05c93409e1600ec7ef3098eb74060b7432981 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/string.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
index b67bae7091d7ecf9d5319b976c10be5e6045caa2..ac232f456396c8eb1e0ae6ae043df7db3b963a06 100644 (file)
@@ -816,7 +816,7 @@ SYM_CODE_START(ret_from_fork)
 
        /* kernel thread */
 1:     movl    %edi, %eax
-       CALL_NOSPEC %ebx
+       CALL_NOSPEC ebx
        /*
         * A kernel thread is allowed to return here after successfully
         * calling do_execve().  Exit to userspace to complete the execve()
@@ -1501,7 +1501,7 @@ SYM_CODE_START_LOCAL_NOALIGN(common_exception_read_cr2)
 
        TRACE_IRQS_OFF
        movl    %esp, %eax                      # pt_regs pointer
-       CALL_NOSPEC %edi
+       CALL_NOSPEC edi
        jmp     ret_from_exception
 SYM_CODE_END(common_exception_read_cr2)
 
@@ -1522,7 +1522,7 @@ SYM_CODE_START_LOCAL_NOALIGN(common_exception)
 
        TRACE_IRQS_OFF
        movl    %esp, %eax                      # pt_regs pointer
-       CALL_NOSPEC %edi
+       CALL_NOSPEC edi
        jmp     ret_from_exception
 SYM_CODE_END(common_exception)
 
@@ -1536,7 +1536,6 @@ SYM_CODE_START(debug)
        jmp     common_exception
 SYM_CODE_END(debug)
 
-#ifdef CONFIG_DOUBLEFAULT
 SYM_CODE_START(double_fault)
 1:
        /*
@@ -1576,7 +1575,6 @@ SYM_CODE_START(double_fault)
        hlt
        jmp 1b
 SYM_CODE_END(double_fault)
-#endif
 
 /*
  * NMI is doubly nasty.  It can happen on the first instruction of
index 3063aa9090f9a7927143fb929fde7ec7a8150d81..64fe3d82157e631c6940056f8bf7efecfa454716 100644 (file)
@@ -348,7 +348,7 @@ SYM_CODE_START(ret_from_fork)
        /* kernel thread */
        UNWIND_HINT_EMPTY
        movq    %r12, %rdi
-       CALL_NOSPEC %rbx
+       CALL_NOSPEC rbx
        /*
         * A kernel thread is allowed to return here after successfully
         * calling do_execve().  Exit to userspace to complete the execve()
index 54581ac671b41ab0280f5690df817d7c2763283b..d8f8a1a69ed11f7e11bbba2bb17301dc7501cae1 100644 (file)
 435    i386    clone3                  sys_clone3
 437    i386    openat2                 sys_openat2
 438    i386    pidfd_getfd             sys_pidfd_getfd
+439    i386    faccessat2              sys_faccessat2
index 37b844f839bc4f4b07f292a26ee5d5987ccdcd1c..78847b32e1370f56f273020e64a36e0a054bded4 100644 (file)
 435    common  clone3                  sys_clone3
 437    common  openat2                 sys_openat2
 438    common  pidfd_getfd             sys_pidfd_getfd
+439    common  faccessat2              sys_faccessat2
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index 433a1259f61d72b56c9c34873f6e72a5f151a4ea..54e03ab26ff3409ec3b86d7c532f071c49ab93a3 100644 (file)
@@ -24,6 +24,8 @@ VDSO32-$(CONFIG_IA32_EMULATION)       := y
 
 # files to link into the vdso
 vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
+vobjs32-y := vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
+vobjs32-y += vdso32/vclock_gettime.o
 
 # files to link into kernel
 obj-y                          += vma.o
@@ -37,10 +39,12 @@ vdso_img-$(VDSO32-y)                += 32
 obj-$(VDSO32-y)                        += vdso32-setup.o
 
 vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
+vobjs32 := $(foreach F,$(vobjs32-y),$(obj)/$F)
 
 $(obj)/vdso.o: $(obj)/vdso.so
 
 targets += vdso.lds $(vobjs-y)
+targets += vdso32/vdso32.lds $(vobjs32-y)
 
 # Build the vDSO image C files and link them in.
 vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o)
@@ -130,10 +134,6 @@ $(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE
 CPPFLAGS_vdso32/vdso32.lds = $(CPPFLAGS_vdso.lds)
 VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -soname linux-gate.so.1
 
-targets += vdso32/vdso32.lds
-targets += vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
-targets += vdso32/vclock_gettime.o
-
 KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO
 $(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
 $(obj)/vdso32.so.dbg: asflags-$(CONFIG_X86_64) += -m32
@@ -158,12 +158,7 @@ endif
 
 $(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
 
-$(obj)/vdso32.so.dbg: FORCE \
-                     $(obj)/vdso32/vdso32.lds \
-                     $(obj)/vdso32/vclock_gettime.o \
-                     $(obj)/vdso32/note.o \
-                     $(obj)/vdso32/system_call.o \
-                     $(obj)/vdso32/sigreturn.o
+$(obj)/vdso32.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32) FORCE
        $(call if_changed,vdso_and_check)
 
 #
index 3842873b3ae3b6c5af587544e8e4c4804ea1f3af..7380908045c7805ebf98b568a11d4c591aafdaf1 100644 (file)
@@ -187,7 +187,7 @@ static void map_input(const char *name, void **addr, size_t *len, int prot)
 
        int fd = open(name, O_RDONLY);
        if (fd == -1)
-               err(1, "%s", name);
+               err(1, "open(%s)", name);
 
        tmp_len = lseek(fd, 0, SEEK_END);
        if (tmp_len == (off_t)-1)
@@ -240,7 +240,7 @@ int main(int argc, char **argv)
        outfilename = argv[3];
        outfile = fopen(outfilename, "w");
        if (!outfile)
-               err(1, "%s", argv[2]);
+               err(1, "fopen(%s)", outfilename);
 
        go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name);
 
index a20b134de2a891d52aa9b88b59d4e78fbc13fc6b..6f46e11ce5390248c4f08d1260fdde7ef74b5b2b 100644 (file)
@@ -13,8 +13,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
        unsigned long load_size = -1;  /* Work around bogus warning */
        unsigned long mapping_size;
        ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
-       int i;
-       unsigned long j;
+       unsigned long i, syms_nr;
        ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
                *alt_sec = NULL;
        ELF(Dyn) *dyn = 0, *dyn_end = 0;
@@ -86,11 +85,10 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
        strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
                GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link);
 
+       syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
        /* Walk the symbol table */
-       for (i = 0;
-            i < GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
-            i++) {
-               int k;
+       for (i = 0; i < syms_nr; i++) {
+               unsigned int k;
                ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) +
                        GET_LE(&symtab_hdr->sh_entsize) * i;
                const char *sym_name = raw_addr +
@@ -150,11 +148,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
        fprintf(outfile,
                "static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
                mapping_size);
-       for (j = 0; j < stripped_len; j++) {
-               if (j % 10 == 0)
+       for (i = 0; i < stripped_len; i++) {
+               if (i % 10 == 0)
                        fprintf(outfile, "\n\t");
                fprintf(outfile, "0x%02X, ",
-                       (int)((unsigned char *)stripped_addr)[j]);
+                       (int)((unsigned char *)stripped_addr)[i]);
        }
        fprintf(outfile, "\n};\n\n");
 
index 9a7a1446cb3a0ebc4993f19b0c690ad4e5bd55e4..4a809c6cbd2f5d8d1b19b187b2a8a0fbfd95092d 100644 (file)
@@ -10,11 +10,11 @@ config PERF_EVENTS_INTEL_UNCORE
        available on NehalemEX and more modern processors.
 
 config PERF_EVENTS_INTEL_RAPL
-       tristate "Intel rapl performance events"
-       depends on PERF_EVENTS && CPU_SUP_INTEL && PCI
+       tristate "Intel/AMD rapl performance events"
+       depends on PERF_EVENTS && (CPU_SUP_INTEL || CPU_SUP_AMD) && PCI
        default y
        ---help---
-       Include support for Intel rapl performance events for power
+       Include support for Intel and AMD rapl performance events for power
        monitoring on modern processors.
 
 config PERF_EVENTS_INTEL_CSTATE
index 9e07f554333fbd318b07fd459ccbb9926c0cbbee..12c42eba77ec3a3ba54b9dcb8bfc709b55b967d1 100644 (file)
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y                                  += core.o probe.o
+obj-$(PERF_EVENTS_INTEL_RAPL)          += rapl.o
 obj-y                                  += amd/
 obj-$(CONFIG_X86_LOCAL_APIC)            += msr.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += intel/
+obj-$(CONFIG_CPU_SUP_CENTAUR)          += zhaoxin/
+obj-$(CONFIG_CPU_SUP_ZHAOXIN)          += zhaoxin/
index a619763e96e16fdc2ce77a1143813333f2c3f67e..9e63ee50b19a69734e79884a640db9940c647430 100644 (file)
@@ -1839,6 +1839,10 @@ static int __init init_hw_perf_events(void)
                err = amd_pmu_init();
                x86_pmu.name = "HYGON";
                break;
+       case X86_VENDOR_ZHAOXIN:
+       case X86_VENDOR_CENTAUR:
+               err = zhaoxin_pmu_init();
+               break;
        default:
                err = -ENOTSUPP;
        }
index 3468b0c1dc7c9553a50eb65836ce777bf0e8ea64..e67a5886336c101b65c7a83769157fcf3fd3d648 100644 (file)
@@ -2,8 +2,6 @@
 obj-$(CONFIG_CPU_SUP_INTEL)            += core.o bts.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += ds.o knc.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += lbr.o p4.o p6.o pt.o
-obj-$(CONFIG_PERF_EVENTS_INTEL_RAPL)   += intel-rapl-perf.o
-intel-rapl-perf-objs                   := rapl.o
 obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += intel-uncore.o
 intel-uncore-objs                      := uncore.o uncore_nhmex.o uncore_snb.o uncore_snbep.o
 obj-$(CONFIG_PERF_EVENTS_INTEL_CSTATE) += intel-cstate.o
index 6a3b599ee0fe7df0fe8010218100e8f9db08acf5..731dd8d0dbb11cc538899b181e32c92aa916900e 100644 (file)
@@ -58,7 +58,7 @@ struct bts_buffer {
        local_t         head;
        unsigned long   end;
        void            **data_pages;
-       struct bts_phys buf[0];
+       struct bts_phys buf[];
 };
 
 static struct pmu bts_pmu;
index 332954cccece7154030db7938cb9632ca27db82b..ca35c8b5ee108a9c113fbedaa13170638ca24bc9 100644 (file)
@@ -1892,8 +1892,8 @@ static __initconst const u64 tnt_hw_cache_extra_regs
 
 static struct extra_reg intel_tnt_extra_regs[] __read_mostly = {
        /* must define OFFCORE_RSP_X first, see intel_fixup_er() */
-       INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0xffffff9fffull, RSP_0),
-       INTEL_UEVENT_EXTRA_REG(0x02b7, MSR_OFFCORE_RSP_1, 0xffffff9fffull, RSP_1),
+       INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0x800ff0ffffff9fffull, RSP_0),
+       INTEL_UEVENT_EXTRA_REG(0x02b7, MSR_OFFCORE_RSP_1, 0xff0ffffff9fffull, RSP_1),
        EVENT_EXTRA_END
 };
 
index 1db7a51d9792b4b6473ce49d0d2d4a90a6d45043..e94af4a54d0d89fb385a61472d3c91708101fd1f 100644 (file)
@@ -226,8 +226,6 @@ static int __init pt_pmu_hw_init(void)
                        pt_pmu.vmx = true;
        }
 
-       attrs = NULL;
-
        for (i = 0; i < PT_CPUID_LEAVES; i++) {
                cpuid_count(20, i,
                            &pt_pmu.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM],
index 0da4a4605536af103dbcfd58831637ae48e49d99..b469ddd45515c86ff3ed1d20dec76492e2945bc0 100644 (file)
@@ -130,7 +130,7 @@ struct intel_uncore_box {
        struct list_head list;
        struct list_head active_list;
        void __iomem *io_addr;
-       struct intel_uncore_extra_reg shared_regs[0];
+       struct intel_uncore_extra_reg shared_regs[];
 };
 
 /* CFL uncore 8th cbox MSRs */
index f1cd1ca1a77b8cdaf02e53db1c2a655a3f4d7c4f..e17a3d8a47ede37fb4eddc48c2ba0159e919f0ed 100644 (file)
@@ -618,6 +618,7 @@ struct x86_pmu {
 
        /* PMI handler bits */
        unsigned int    late_ack                :1,
+                       enabled_ack             :1,
                        counter_freezing        :1;
        /*
         * sysfs attrs
@@ -1133,3 +1134,12 @@ static inline int is_ht_workaround_enabled(void)
        return 0;
 }
 #endif /* CONFIG_CPU_SUP_INTEL */
+
+#if ((defined CONFIG_CPU_SUP_CENTAUR) || (defined CONFIG_CPU_SUP_ZHAOXIN))
+int zhaoxin_pmu_init(void);
+#else
+static inline int zhaoxin_pmu_init(void)
+{
+       return 0;
+}
+#endif /*CONFIG_CPU_SUP_CENTAUR or CONFIG_CPU_SUP_ZHAOXIN*/
index c2ede2f3b27702ae4dab76217626447e7b05ec70..136a1e847254eab25b3236c42b633e038431a6d7 100644 (file)
@@ -10,6 +10,11 @@ not_visible(struct kobject *kobj, struct attribute *attr, int i)
        return 0;
 }
 
+/*
+ * Accepts msr[] array with non populated entries as long as either
+ * msr[i].msr is 0 or msr[i].grp is NULL. Note that the default sysfs
+ * visibility is visible when group->is_visible callback is set.
+ */
 unsigned long
 perf_msr_probe(struct perf_msr *msr, int cnt, bool zero, void *data)
 {
@@ -24,8 +29,16 @@ perf_msr_probe(struct perf_msr *msr, int cnt, bool zero, void *data)
                if (!msr[bit].no_check) {
                        struct attribute_group *grp = msr[bit].grp;
 
+                       /* skip entry with no group */
+                       if (!grp)
+                               continue;
+
                        grp->is_visible = not_visible;
 
+                       /* skip unpopulated entry */
+                       if (!msr[bit].msr)
+                               continue;
+
                        if (msr[bit].test && !msr[bit].test(bit, data))
                                continue;
                        /* Virt sucks; you cannot tell if a R/O MSR is present :/ */
similarity index 91%
rename from arch/x86/events/intel/rapl.c
rename to arch/x86/events/rapl.c
index a5dbd25852cb75b69b67f936e294db73aac95f92..0f2bf59f43541031e9595ff5521401545b82c882 100644 (file)
@@ -1,11 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Support Intel RAPL energy consumption counters
+ * Support Intel/AMD RAPL energy consumption counters
  * Copyright (C) 2013 Google, Inc., Stephane Eranian
  *
  * Intel RAPL interface is specified in the IA-32 Manual Vol3b
  * section 14.7.1 (September 2013)
  *
+ * AMD RAPL interface for Fam17h is described in the public PPR:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=206537
+ *
  * RAPL provides more controls than just reporting energy consumption
  * however here we only expose the 3 energy consumption free running
  * counters (pp0, pkg, dram).
@@ -58,8 +61,8 @@
 #include <linux/nospec.h>
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include "../perf_event.h"
-#include "../probe.h"
+#include "perf_event.h"
+#include "probe.h"
 
 MODULE_LICENSE("GPL");
 
@@ -128,7 +131,9 @@ struct rapl_pmus {
 };
 
 struct rapl_model {
+       struct perf_msr *rapl_msrs;
        unsigned long   events;
+       unsigned int    msr_power_unit;
        bool            apply_quirk;
 };
 
@@ -138,7 +143,7 @@ static struct rapl_pmus *rapl_pmus;
 static cpumask_t rapl_cpu_mask;
 static unsigned int rapl_cntr_mask;
 static u64 rapl_timer_ms;
-static struct perf_msr rapl_msrs[];
+static struct perf_msr *rapl_msrs;
 
 static inline struct rapl_pmu *cpu_to_rapl_pmu(unsigned int cpu)
 {
@@ -455,9 +460,16 @@ static struct attribute *rapl_events_cores[] = {
        NULL,
 };
 
+static umode_t
+rapl_not_visible(struct kobject *kobj, struct attribute *attr, int i)
+{
+       return 0;
+}
+
 static struct attribute_group rapl_events_cores_group = {
        .name  = "events",
        .attrs = rapl_events_cores,
+       .is_visible = rapl_not_visible,
 };
 
 static struct attribute *rapl_events_pkg[] = {
@@ -470,6 +482,7 @@ static struct attribute *rapl_events_pkg[] = {
 static struct attribute_group rapl_events_pkg_group = {
        .name  = "events",
        .attrs = rapl_events_pkg,
+       .is_visible = rapl_not_visible,
 };
 
 static struct attribute *rapl_events_ram[] = {
@@ -482,6 +495,7 @@ static struct attribute *rapl_events_ram[] = {
 static struct attribute_group rapl_events_ram_group = {
        .name  = "events",
        .attrs = rapl_events_ram,
+       .is_visible = rapl_not_visible,
 };
 
 static struct attribute *rapl_events_gpu[] = {
@@ -494,6 +508,7 @@ static struct attribute *rapl_events_gpu[] = {
 static struct attribute_group rapl_events_gpu_group = {
        .name  = "events",
        .attrs = rapl_events_gpu,
+       .is_visible = rapl_not_visible,
 };
 
 static struct attribute *rapl_events_psys[] = {
@@ -506,6 +521,7 @@ static struct attribute *rapl_events_psys[] = {
 static struct attribute_group rapl_events_psys_group = {
        .name  = "events",
        .attrs = rapl_events_psys,
+       .is_visible = rapl_not_visible,
 };
 
 static bool test_msr(int idx, void *data)
@@ -513,7 +529,7 @@ static bool test_msr(int idx, void *data)
        return test_bit(idx, (unsigned long *) data);
 }
 
-static struct perf_msr rapl_msrs[] = {
+static struct perf_msr intel_rapl_msrs[] = {
        [PERF_RAPL_PP0]  = { MSR_PP0_ENERGY_STATUS,      &rapl_events_cores_group, test_msr },
        [PERF_RAPL_PKG]  = { MSR_PKG_ENERGY_STATUS,      &rapl_events_pkg_group,   test_msr },
        [PERF_RAPL_RAM]  = { MSR_DRAM_ENERGY_STATUS,     &rapl_events_ram_group,   test_msr },
@@ -521,6 +537,16 @@ static struct perf_msr rapl_msrs[] = {
        [PERF_RAPL_PSYS] = { MSR_PLATFORM_ENERGY_STATUS, &rapl_events_psys_group,  test_msr },
 };
 
+/*
+ * Force to PERF_RAPL_MAX size due to:
+ * - perf_msr_probe(PERF_RAPL_MAX)
+ * - want to use same event codes across both architectures
+ */
+static struct perf_msr amd_rapl_msrs[PERF_RAPL_MAX] = {
+       [PERF_RAPL_PKG]  = { MSR_AMD_PKG_ENERGY_STATUS,  &rapl_events_pkg_group,   test_msr },
+};
+
+
 static int rapl_cpu_offline(unsigned int cpu)
 {
        struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu);
@@ -575,13 +601,13 @@ static int rapl_cpu_online(unsigned int cpu)
        return 0;
 }
 
-static int rapl_check_hw_unit(bool apply_quirk)
+static int rapl_check_hw_unit(struct rapl_model *rm)
 {
        u64 msr_rapl_power_unit_bits;
        int i;
 
        /* protect rdmsrl() to handle virtualization */
-       if (rdmsrl_safe(MSR_RAPL_POWER_UNIT, &msr_rapl_power_unit_bits))
+       if (rdmsrl_safe(rm->msr_power_unit, &msr_rapl_power_unit_bits))
                return -1;
        for (i = 0; i < NR_RAPL_DOMAINS; i++)
                rapl_hw_unit[i] = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
@@ -592,7 +618,7 @@ static int rapl_check_hw_unit(bool apply_quirk)
         * "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, V2
         * of 2. Datasheet, September 2014, Reference Number: 330784-001 "
         */
-       if (apply_quirk)
+       if (rm->apply_quirk)
                rapl_hw_unit[PERF_RAPL_RAM] = 16;
 
        /*
@@ -673,6 +699,8 @@ static struct rapl_model model_snb = {
                          BIT(PERF_RAPL_PKG) |
                          BIT(PERF_RAPL_PP1),
        .apply_quirk    = false,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
 };
 
 static struct rapl_model model_snbep = {
@@ -680,6 +708,8 @@ static struct rapl_model model_snbep = {
                          BIT(PERF_RAPL_PKG) |
                          BIT(PERF_RAPL_RAM),
        .apply_quirk    = false,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
 };
 
 static struct rapl_model model_hsw = {
@@ -688,6 +718,8 @@ static struct rapl_model model_hsw = {
                          BIT(PERF_RAPL_RAM) |
                          BIT(PERF_RAPL_PP1),
        .apply_quirk    = false,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
 };
 
 static struct rapl_model model_hsx = {
@@ -695,12 +727,16 @@ static struct rapl_model model_hsx = {
                          BIT(PERF_RAPL_PKG) |
                          BIT(PERF_RAPL_RAM),
        .apply_quirk    = true,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
 };
 
 static struct rapl_model model_knl = {
        .events         = BIT(PERF_RAPL_PKG) |
                          BIT(PERF_RAPL_RAM),
        .apply_quirk    = true,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
 };
 
 static struct rapl_model model_skl = {
@@ -710,6 +746,15 @@ static struct rapl_model model_skl = {
                          BIT(PERF_RAPL_PP1) |
                          BIT(PERF_RAPL_PSYS),
        .apply_quirk    = false,
+       .msr_power_unit = MSR_RAPL_POWER_UNIT,
+       .rapl_msrs      = intel_rapl_msrs,
+};
+
+static struct rapl_model model_amd_fam17h = {
+       .events         = BIT(PERF_RAPL_PKG),
+       .apply_quirk    = false,
+       .msr_power_unit = MSR_AMD_RAPL_POWER_UNIT,
+       .rapl_msrs      = amd_rapl_msrs,
 };
 
 static const struct x86_cpu_id rapl_model_match[] __initconst = {
@@ -738,8 +783,11 @@ static const struct x86_cpu_id rapl_model_match[] __initconst = {
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS,  &model_hsw),
        X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L,           &model_skl),
        X86_MATCH_INTEL_FAM6_MODEL(ICELAKE,             &model_skl),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,           &model_hsx),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,           &model_hsx),
        X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L,         &model_skl),
        X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE,           &model_skl),
+       X86_MATCH_VENDOR_FAM(AMD, 0x17, &model_amd_fam17h),
        {},
 };
 MODULE_DEVICE_TABLE(x86cpu, rapl_model_match);
@@ -755,10 +803,13 @@ static int __init rapl_pmu_init(void)
                return -ENODEV;
 
        rm = (struct rapl_model *) id->driver_data;
+
+       rapl_msrs = rm->rapl_msrs;
+
        rapl_cntr_mask = perf_msr_probe(rapl_msrs, PERF_RAPL_MAX,
                                        false, (void *) &rm->events);
 
-       ret = rapl_check_hw_unit(rm->apply_quirk);
+       ret = rapl_check_hw_unit(rm);
        if (ret)
                return ret;
 
diff --git a/arch/x86/events/zhaoxin/Makefile b/arch/x86/events/zhaoxin/Makefile
new file mode 100644 (file)
index 0000000..642c117
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y  += core.o
diff --git a/arch/x86/events/zhaoxin/core.c b/arch/x86/events/zhaoxin/core.c
new file mode 100644 (file)
index 0000000..898fa1a
--- /dev/null
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Zhoaxin PMU; like Intel Architectural PerfMon-v2
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/nmi.h>
+
+#include <asm/cpufeature.h>
+#include <asm/hardirq.h>
+#include <asm/apic.h>
+
+#include "../perf_event.h"
+
+/*
+ * Zhaoxin PerfMon, used on zxc and later.
+ */
+static u64 zx_pmon_event_map[PERF_COUNT_HW_MAX] __read_mostly = {
+
+       [PERF_COUNT_HW_CPU_CYCLES]        = 0x0082,
+       [PERF_COUNT_HW_INSTRUCTIONS]      = 0x00c0,
+       [PERF_COUNT_HW_CACHE_REFERENCES]  = 0x0515,
+       [PERF_COUNT_HW_CACHE_MISSES]      = 0x051a,
+       [PERF_COUNT_HW_BUS_CYCLES]        = 0x0083,
+};
+
+static struct event_constraint zxc_event_constraints[] __read_mostly = {
+
+       FIXED_EVENT_CONSTRAINT(0x0082, 1), /* unhalted core clock cycles */
+       EVENT_CONSTRAINT_END
+};
+
+static struct event_constraint zxd_event_constraints[] __read_mostly = {
+
+       FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* retired instructions */
+       FIXED_EVENT_CONSTRAINT(0x0082, 1), /* unhalted core clock cycles */
+       FIXED_EVENT_CONSTRAINT(0x0083, 2), /* unhalted bus clock cycles */
+       EVENT_CONSTRAINT_END
+};
+
+static __initconst const u64 zxd_hw_cache_event_ids
+                               [PERF_COUNT_HW_CACHE_MAX]
+                               [PERF_COUNT_HW_CACHE_OP_MAX]
+                               [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0042,
+               [C(RESULT_MISS)] = 0x0538,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = 0x0043,
+               [C(RESULT_MISS)] = 0x0562,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(L1I)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0300,
+               [C(RESULT_MISS)] = 0x0301,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = 0x030a,
+               [C(RESULT_MISS)] = 0x030b,
+       },
+},
+[C(LL)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(DTLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0042,
+               [C(RESULT_MISS)] = 0x052c,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = 0x0043,
+               [C(RESULT_MISS)] = 0x0530,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = 0x0564,
+               [C(RESULT_MISS)] = 0x0565,
+       },
+},
+[C(ITLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x00c0,
+               [C(RESULT_MISS)] = 0x0534,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(BPU)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0700,
+               [C(RESULT_MISS)] = 0x0709,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(NODE)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+};
+
+static __initconst const u64 zxe_hw_cache_event_ids
+                               [PERF_COUNT_HW_CACHE_MAX]
+                               [PERF_COUNT_HW_CACHE_OP_MAX]
+                               [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+[C(L1D)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0568,
+               [C(RESULT_MISS)] = 0x054b,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = 0x0669,
+               [C(RESULT_MISS)] = 0x0562,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(L1I)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0300,
+               [C(RESULT_MISS)] = 0x0301,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = 0x030a,
+               [C(RESULT_MISS)] = 0x030b,
+       },
+},
+[C(LL)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0,
+               [C(RESULT_MISS)] = 0x0,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = 0x0,
+               [C(RESULT_MISS)] = 0x0,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = 0x0,
+               [C(RESULT_MISS)] = 0x0,
+       },
+},
+[C(DTLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0568,
+               [C(RESULT_MISS)] = 0x052c,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = 0x0669,
+               [C(RESULT_MISS)] = 0x0530,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = 0x0564,
+               [C(RESULT_MISS)] = 0x0565,
+       },
+},
+[C(ITLB)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x00c0,
+               [C(RESULT_MISS)] = 0x0534,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(BPU)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = 0x0028,
+               [C(RESULT_MISS)] = 0x0029,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+[C(NODE)] = {
+       [C(OP_READ)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_WRITE)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+       [C(OP_PREFETCH)] = {
+               [C(RESULT_ACCESS)] = -1,
+               [C(RESULT_MISS)] = -1,
+       },
+},
+};
+
+static void zhaoxin_pmu_disable_all(void)
+{
+       wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
+}
+
+static void zhaoxin_pmu_enable_all(int added)
+{
+       wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
+}
+
+static inline u64 zhaoxin_pmu_get_status(void)
+{
+       u64 status;
+
+       rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
+
+       return status;
+}
+
+static inline void zhaoxin_pmu_ack_status(u64 ack)
+{
+       wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
+}
+
+static inline void zxc_pmu_ack_status(u64 ack)
+{
+       /*
+        * ZXC needs global control enabled in order to clear status bits.
+        */
+       zhaoxin_pmu_enable_all(0);
+       zhaoxin_pmu_ack_status(ack);
+       zhaoxin_pmu_disable_all();
+}
+
+static void zhaoxin_pmu_disable_fixed(struct hw_perf_event *hwc)
+{
+       int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
+       u64 ctrl_val, mask;
+
+       mask = 0xfULL << (idx * 4);
+
+       rdmsrl(hwc->config_base, ctrl_val);
+       ctrl_val &= ~mask;
+       wrmsrl(hwc->config_base, ctrl_val);
+}
+
+static void zhaoxin_pmu_disable_event(struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) {
+               zhaoxin_pmu_disable_fixed(hwc);
+               return;
+       }
+
+       x86_pmu_disable_event(event);
+}
+
+static void zhaoxin_pmu_enable_fixed(struct hw_perf_event *hwc)
+{
+       int idx = hwc->idx - INTEL_PMC_IDX_FIXED;
+       u64 ctrl_val, bits, mask;
+
+       /*
+        * Enable IRQ generation (0x8),
+        * and enable ring-3 counting (0x2) and ring-0 counting (0x1)
+        * if requested:
+        */
+       bits = 0x8ULL;
+       if (hwc->config & ARCH_PERFMON_EVENTSEL_USR)
+               bits |= 0x2;
+       if (hwc->config & ARCH_PERFMON_EVENTSEL_OS)
+               bits |= 0x1;
+
+       bits <<= (idx * 4);
+       mask = 0xfULL << (idx * 4);
+
+       rdmsrl(hwc->config_base, ctrl_val);
+       ctrl_val &= ~mask;
+       ctrl_val |= bits;
+       wrmsrl(hwc->config_base, ctrl_val);
+}
+
+static void zhaoxin_pmu_enable_event(struct perf_event *event)
+{
+       struct hw_perf_event *hwc = &event->hw;
+
+       if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) {
+               zhaoxin_pmu_enable_fixed(hwc);
+               return;
+       }
+
+       __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
+}
+
+/*
+ * This handler is triggered by the local APIC, so the APIC IRQ handling
+ * rules apply:
+ */
+static int zhaoxin_pmu_handle_irq(struct pt_regs *regs)
+{
+       struct perf_sample_data data;
+       struct cpu_hw_events *cpuc;
+       int handled = 0;
+       u64 status;
+       int bit;
+
+       cpuc = this_cpu_ptr(&cpu_hw_events);
+       apic_write(APIC_LVTPC, APIC_DM_NMI);
+       zhaoxin_pmu_disable_all();
+       status = zhaoxin_pmu_get_status();
+       if (!status)
+               goto done;
+
+again:
+       if (x86_pmu.enabled_ack)
+               zxc_pmu_ack_status(status);
+       else
+               zhaoxin_pmu_ack_status(status);
+
+       inc_irq_stat(apic_perf_irqs);
+
+       /*
+        * CondChgd bit 63 doesn't mean any overflow status. Ignore
+        * and clear the bit.
+        */
+       if (__test_and_clear_bit(63, (unsigned long *)&status)) {
+               if (!status)
+                       goto done;
+       }
+
+       for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
+               struct perf_event *event = cpuc->events[bit];
+
+               handled++;
+
+               if (!test_bit(bit, cpuc->active_mask))
+                       continue;
+
+               x86_perf_event_update(event);
+               perf_sample_data_init(&data, 0, event->hw.last_period);
+
+               if (!x86_perf_event_set_period(event))
+                       continue;
+
+               if (perf_event_overflow(event, &data, regs))
+                       x86_pmu_stop(event, 0);
+       }
+
+       /*
+        * Repeat if there is more work to be done:
+        */
+       status = zhaoxin_pmu_get_status();
+       if (status)
+               goto again;
+
+done:
+       zhaoxin_pmu_enable_all(0);
+       return handled;
+}
+
+static u64 zhaoxin_pmu_event_map(int hw_event)
+{
+       return zx_pmon_event_map[hw_event];
+}
+
+static struct event_constraint *
+zhaoxin_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
+                       struct perf_event *event)
+{
+       struct event_constraint *c;
+
+       if (x86_pmu.event_constraints) {
+               for_each_event_constraint(c, x86_pmu.event_constraints) {
+                       if ((event->hw.config & c->cmask) == c->code)
+                               return c;
+               }
+       }
+
+       return &unconstrained;
+}
+
+PMU_FORMAT_ATTR(event, "config:0-7");
+PMU_FORMAT_ATTR(umask, "config:8-15");
+PMU_FORMAT_ATTR(edge,  "config:18");
+PMU_FORMAT_ATTR(inv,   "config:23");
+PMU_FORMAT_ATTR(cmask, "config:24-31");
+
+static struct attribute *zx_arch_formats_attr[] = {
+       &format_attr_event.attr,
+       &format_attr_umask.attr,
+       &format_attr_edge.attr,
+       &format_attr_inv.attr,
+       &format_attr_cmask.attr,
+       NULL,
+};
+
+static ssize_t zhaoxin_event_sysfs_show(char *page, u64 config)
+{
+       u64 event = (config & ARCH_PERFMON_EVENTSEL_EVENT);
+
+       return x86_event_sysfs_show(page, config, event);
+}
+
+static const struct x86_pmu zhaoxin_pmu __initconst = {
+       .name                   = "zhaoxin",
+       .handle_irq             = zhaoxin_pmu_handle_irq,
+       .disable_all            = zhaoxin_pmu_disable_all,
+       .enable_all             = zhaoxin_pmu_enable_all,
+       .enable                 = zhaoxin_pmu_enable_event,
+       .disable                = zhaoxin_pmu_disable_event,
+       .hw_config              = x86_pmu_hw_config,
+       .schedule_events        = x86_schedule_events,
+       .eventsel               = MSR_ARCH_PERFMON_EVENTSEL0,
+       .perfctr                = MSR_ARCH_PERFMON_PERFCTR0,
+       .event_map              = zhaoxin_pmu_event_map,
+       .max_events             = ARRAY_SIZE(zx_pmon_event_map),
+       .apic                   = 1,
+       /*
+        * For zxd/zxe, read/write operation for PMCx MSR is 48 bits.
+        */
+       .max_period             = (1ULL << 47) - 1,
+       .get_event_constraints  = zhaoxin_get_event_constraints,
+
+       .format_attrs           = zx_arch_formats_attr,
+       .events_sysfs_show      = zhaoxin_event_sysfs_show,
+};
+
+static const struct { int id; char *name; } zx_arch_events_map[] __initconst = {
+       { PERF_COUNT_HW_CPU_CYCLES, "cpu cycles" },
+       { PERF_COUNT_HW_INSTRUCTIONS, "instructions" },
+       { PERF_COUNT_HW_BUS_CYCLES, "bus cycles" },
+       { PERF_COUNT_HW_CACHE_REFERENCES, "cache references" },
+       { PERF_COUNT_HW_CACHE_MISSES, "cache misses" },
+       { PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branch instructions" },
+       { PERF_COUNT_HW_BRANCH_MISSES, "branch misses" },
+};
+
+static __init void zhaoxin_arch_events_quirk(void)
+{
+       int bit;
+
+       /* disable event that reported as not presend by cpuid */
+       for_each_set_bit(bit, x86_pmu.events_mask, ARRAY_SIZE(zx_arch_events_map)) {
+               zx_pmon_event_map[zx_arch_events_map[bit].id] = 0;
+               pr_warn("CPUID marked event: \'%s\' unavailable\n",
+                       zx_arch_events_map[bit].name);
+       }
+}
+
+__init int zhaoxin_pmu_init(void)
+{
+       union cpuid10_edx edx;
+       union cpuid10_eax eax;
+       union cpuid10_ebx ebx;
+       struct event_constraint *c;
+       unsigned int unused;
+       int version;
+
+       pr_info("Welcome to zhaoxin pmu!\n");
+
+       /*
+        * Check whether the Architectural PerfMon supports
+        * hw_event or not.
+        */
+       cpuid(10, &eax.full, &ebx.full, &unused, &edx.full);
+
+       if (eax.split.mask_length < ARCH_PERFMON_EVENTS_COUNT - 1)
+               return -ENODEV;
+
+       version = eax.split.version_id;
+       if (version != 2)
+               return -ENODEV;
+
+       x86_pmu = zhaoxin_pmu;
+       pr_info("Version check pass!\n");
+
+       x86_pmu.version                 = version;
+       x86_pmu.num_counters            = eax.split.num_counters;
+       x86_pmu.cntval_bits             = eax.split.bit_width;
+       x86_pmu.cntval_mask             = (1ULL << eax.split.bit_width) - 1;
+       x86_pmu.events_maskl            = ebx.full;
+       x86_pmu.events_mask_len         = eax.split.mask_length;
+
+       x86_pmu.num_counters_fixed = edx.split.num_counters_fixed;
+       x86_add_quirk(zhaoxin_arch_events_quirk);
+
+       switch (boot_cpu_data.x86) {
+       case 0x06:
+               if (boot_cpu_data.x86_model == 0x0f || boot_cpu_data.x86_model == 0x19) {
+
+                       x86_pmu.max_period = x86_pmu.cntval_mask >> 1;
+
+                       /* Clearing status works only if the global control is enable on zxc. */
+                       x86_pmu.enabled_ack = 1;
+
+                       x86_pmu.event_constraints = zxc_event_constraints;
+                       zx_pmon_event_map[PERF_COUNT_HW_INSTRUCTIONS] = 0;
+                       zx_pmon_event_map[PERF_COUNT_HW_CACHE_REFERENCES] = 0;
+                       zx_pmon_event_map[PERF_COUNT_HW_CACHE_MISSES] = 0;
+                       zx_pmon_event_map[PERF_COUNT_HW_BUS_CYCLES] = 0;
+
+                       pr_cont("ZXC events, ");
+                       break;
+               }
+               return -ENODEV;
+
+       case 0x07:
+               zx_pmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =
+                       X86_CONFIG(.event = 0x01, .umask = 0x01, .inv = 0x01, .cmask = 0x01);
+
+               zx_pmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] =
+                       X86_CONFIG(.event = 0x0f, .umask = 0x04, .inv = 0, .cmask = 0);
+
+               switch (boot_cpu_data.x86_model) {
+               case 0x1b:
+                       memcpy(hw_cache_event_ids, zxd_hw_cache_event_ids,
+                              sizeof(hw_cache_event_ids));
+
+                       x86_pmu.event_constraints = zxd_event_constraints;
+
+                       zx_pmon_event_map[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x0700;
+                       zx_pmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x0709;
+
+                       pr_cont("ZXD events, ");
+                       break;
+               case 0x3b:
+                       memcpy(hw_cache_event_ids, zxe_hw_cache_event_ids,
+                              sizeof(hw_cache_event_ids));
+
+                       x86_pmu.event_constraints = zxd_event_constraints;
+
+                       zx_pmon_event_map[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x0028;
+                       zx_pmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x0029;
+
+                       pr_cont("ZXE events, ");
+                       break;
+               default:
+                       return -ENODEV;
+               }
+               break;
+
+       default:
+               return -ENODEV;
+       }
+
+       x86_pmu.intel_ctrl = (1 << (x86_pmu.num_counters)) - 1;
+       x86_pmu.intel_ctrl |= ((1LL << x86_pmu.num_counters_fixed)-1) << INTEL_PMC_IDX_FIXED;
+
+       if (x86_pmu.event_constraints) {
+               for_each_event_constraint(c, x86_pmu.event_constraints) {
+                       c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
+                       c->weight += x86_pmu.num_counters;
+               }
+       }
+
+       return 0;
+}
+
index acf76b466db6b1332e829b1863fa5171f4920664..e2137070386a58b9581569f62e102ebbd145e3e2 100644 (file)
@@ -97,8 +97,7 @@ static int hv_cpu_init(unsigned int cpu)
         * not be stopped in the case of CPU offlining and the VM will hang.
         */
        if (!*hvp) {
-               *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO,
-                                PAGE_KERNEL);
+               *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO);
        }
 
        if (*hvp) {
@@ -379,7 +378,7 @@ void __init hyperv_init(void)
        guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0);
        wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
 
-       hv_hypercall_pg  = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
+       hv_hypercall_pg = vmalloc_exec(PAGE_SIZE);
        if (hv_hypercall_pg == NULL) {
                wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
                goto remove_cpuhp_state;
index 3d21eab7aaedf305683754d37faa46e6a263674c..6efe6cb3768ae8c3040daf34325867ec15a58a87 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <asm/unistd_32.h>
+#include <asm/audit.h>
 
 unsigned ia32_dir_class[] = {
 #include <asm-generic/audit_dir_write.h>
index f9d8804144d0936f3593c2c09b404e9465cc3846..81cf22398cd16c81c5d18338251fced04cb41a6b 100644 (file)
@@ -350,7 +350,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
        unsafe_put_user(*(__u64 *)set, (__u64 *)&frame->uc.uc_sigmask, Efault);
        user_access_end();
 
-       if (__copy_siginfo_to_user32(&frame->info, &ksig->info, false))
+       if (__copy_siginfo_to_user32(&frame->info, &ksig->info))
                return -EFAULT;
 
        /* Set up registers for signal handler */
diff --git a/arch/x86/include/asm/GEN-for-each-reg.h b/arch/x86/include/asm/GEN-for-each-reg.h
new file mode 100644 (file)
index 0000000..1b07fb1
--- /dev/null
@@ -0,0 +1,25 @@
+#ifdef CONFIG_64BIT
+GEN(rax)
+GEN(rbx)
+GEN(rcx)
+GEN(rdx)
+GEN(rsi)
+GEN(rdi)
+GEN(rbp)
+GEN(r8)
+GEN(r9)
+GEN(r10)
+GEN(r11)
+GEN(r12)
+GEN(r13)
+GEN(r14)
+GEN(r15)
+#else
+GEN(eax)
+GEN(ebx)
+GEN(ecx)
+GEN(edx)
+GEN(esi)
+GEN(edi)
+GEN(ebp)
+#endif
index 99bb207fc04c8a54200323c2368015b6ff65e65c..87ce8e963215797de269ab39f3c01ac4c112e910 100644 (file)
 #define APBT_MIN_FREQ          1000000
 #define APBT_MMAP_SIZE         1024
 
-#define APBT_DEV_USED  1
-
 extern void apbt_time_init(void);
-extern unsigned long apbt_quick_calibrate(void);
-extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu);
 extern void apbt_setup_secondary_clock(void);
 
 extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint);
@@ -38,7 +34,6 @@ extern int sfi_mtimer_num;
 
 #else /* CONFIG_APB_TIMER */
 
-static inline unsigned long apbt_quick_calibrate(void) {return 0; }
 static inline void apbt_time_init(void) { }
 
 #endif
index 7a4bb1bd4bdb84356d18e814b704b37f62fbf980..ebc248e495493a62b35a34d90583c56c82dac35c 100644 (file)
 
 #define RDRAND_RETRY_LOOPS     10
 
-#define RDRAND_INT     ".byte 0x0f,0xc7,0xf0"
-#define RDSEED_INT     ".byte 0x0f,0xc7,0xf8"
-#ifdef CONFIG_X86_64
-# define RDRAND_LONG   ".byte 0x48,0x0f,0xc7,0xf0"
-# define RDSEED_LONG   ".byte 0x48,0x0f,0xc7,0xf8"
-#else
-# define RDRAND_LONG   RDRAND_INT
-# define RDSEED_LONG   RDSEED_INT
-#endif
-
 /* Unconditional execution of RDRAND and RDSEED */
 
 static inline bool __must_check rdrand_long(unsigned long *v)
@@ -32,9 +22,9 @@ static inline bool __must_check rdrand_long(unsigned long *v)
        bool ok;
        unsigned int retry = RDRAND_RETRY_LOOPS;
        do {
-               asm volatile(RDRAND_LONG
+               asm volatile("rdrand %[out]"
                             CC_SET(c)
-                            : CC_OUT(c) (ok), "=a" (*v));
+                            : CC_OUT(c) (ok), [out] "=r" (*v));
                if (ok)
                        return true;
        } while (--retry);
@@ -46,9 +36,9 @@ static inline bool __must_check rdrand_int(unsigned int *v)
        bool ok;
        unsigned int retry = RDRAND_RETRY_LOOPS;
        do {
-               asm volatile(RDRAND_INT
+               asm volatile("rdrand %[out]"
                             CC_SET(c)
-                            : CC_OUT(c) (ok), "=a" (*v));
+                            : CC_OUT(c) (ok), [out] "=r" (*v));
                if (ok)
                        return true;
        } while (--retry);
@@ -58,18 +48,18 @@ static inline bool __must_check rdrand_int(unsigned int *v)
 static inline bool __must_check rdseed_long(unsigned long *v)
 {
        bool ok;
-       asm volatile(RDSEED_LONG
+       asm volatile("rdseed %[out]"
                     CC_SET(c)
-                    : CC_OUT(c) (ok), "=a" (*v));
+                    : CC_OUT(c) (ok), [out] "=r" (*v));
        return ok;
 }
 
 static inline bool __must_check rdseed_int(unsigned int *v)
 {
        bool ok;
-       asm volatile(RDSEED_INT
+       asm volatile("rdseed %[out]"
                     CC_SET(c)
-                    : CC_OUT(c) (ok), "=a" (*v));
+                    : CC_OUT(c) (ok), [out] "=r" (*v));
        return ok;
 }
 
index ce92c4acc913368b8383d85555c6fcac69fcb470..9bf2620ce8172d342a8efae7bacd84410db43660 100644 (file)
@@ -17,24 +17,19 @@ extern void cmpxchg8b_emu(void);
 #endif
 
 #ifdef CONFIG_RETPOLINE
-#ifdef CONFIG_X86_32
-#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_e ## reg(void);
-#else
-#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_r ## reg(void);
-INDIRECT_THUNK(8)
-INDIRECT_THUNK(9)
-INDIRECT_THUNK(10)
-INDIRECT_THUNK(11)
-INDIRECT_THUNK(12)
-INDIRECT_THUNK(13)
-INDIRECT_THUNK(14)
-INDIRECT_THUNK(15)
-#endif
-INDIRECT_THUNK(ax)
-INDIRECT_THUNK(bx)
-INDIRECT_THUNK(cx)
-INDIRECT_THUNK(dx)
-INDIRECT_THUNK(si)
-INDIRECT_THUNK(di)
-INDIRECT_THUNK(bp)
+
+#define DECL_INDIRECT_THUNK(reg) \
+       extern asmlinkage void __x86_indirect_thunk_ ## reg (void);
+
+#define DECL_RETPOLINE(reg) \
+       extern asmlinkage void __x86_retpoline_ ## reg (void);
+
+#undef GEN
+#define GEN(reg) DECL_INDIRECT_THUNK(reg)
+#include <asm/GEN-for-each-reg.h>
+
+#undef GEN
+#define GEN(reg) DECL_RETPOLINE(reg)
+#include <asm/GEN-for-each-reg.h>
+
 #endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/audit.h b/arch/x86/include/asm/audit.h
new file mode 100644 (file)
index 0000000..36aec57
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_AUDIT_H
+#define _ASM_X86_AUDIT_H
+
+int ia32_classify_syscall(unsigned int syscall);
+
+#endif /* _ASM_X86_AUDIT_H */
index d79d1e622dcf1db2717185a12c19e97a6358fa5e..0ada98d5d09f7a476fa44e1493d00db4af4161bc 100644 (file)
@@ -1,4 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#define  _HAVE_ARCH_COPY_AND_CSUM_FROM_USER 1
+#define HAVE_CSUM_COPY_USER
 #ifdef CONFIG_X86_32
 # include <asm/checksum_32.h>
 #else
index f57b94e02c5770f9c33f281b8ff647adc7280e12..11624c8a9d8d375356b5b4ebc88af150a773699e 100644 (file)
@@ -44,18 +44,21 @@ static inline __wsum csum_partial_copy_nocheck(const void *src, void *dst,
        return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
 }
 
-static inline __wsum csum_partial_copy_from_user(const void __user *src,
-                                                void *dst,
-                                                int len, __wsum sum,
-                                                int *err_ptr)
+static inline __wsum csum_and_copy_from_user(const void __user *src,
+                                            void *dst, int len,
+                                            __wsum sum, int *err_ptr)
 {
        __wsum ret;
 
        might_sleep();
-       stac();
+       if (!user_access_begin(src, len)) {
+               if (len)
+                       *err_ptr = -EFAULT;
+               return sum;
+       }
        ret = csum_partial_copy_generic((__force void *)src, dst,
                                        len, sum, err_ptr, NULL);
-       clac();
+       user_access_end();
 
        return ret;
 }
@@ -173,7 +176,6 @@ static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
 /*
  *     Copy and checksum to user
  */
-#define HAVE_CSUM_COPY_USER
 static inline __wsum csum_and_copy_to_user(const void *src,
                                           void __user *dst,
                                           int len, __wsum sum,
@@ -182,11 +184,10 @@ static inline __wsum csum_and_copy_to_user(const void *src,
        __wsum ret;
 
        might_sleep();
-       if (access_ok(dst, len)) {
-               stac();
+       if (user_access_begin(dst, len)) {
                ret = csum_partial_copy_generic(src, (__force void *)dst,
                                                len, sum, NULL, err_ptr);
-               clac();
+               user_access_end();
                return ret;
        }
 
index 3ec6d3267cf9a4a1adcc0384b93be4ef2b1d3d8a..0a289b87e8722a998c85f0a4ae954a5f38f14dd0 100644 (file)
@@ -129,27 +129,19 @@ static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
  */
 extern __wsum csum_partial(const void *buff, int len, __wsum sum);
 
-#define  _HAVE_ARCH_COPY_AND_CSUM_FROM_USER 1
-#define HAVE_CSUM_COPY_USER 1
-
-
 /* Do not call this directly. Use the wrappers below */
 extern __visible __wsum csum_partial_copy_generic(const void *src, const void *dst,
                                        int len, __wsum sum,
                                        int *src_err_ptr, int *dst_err_ptr);
 
 
-extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+extern __wsum csum_and_copy_from_user(const void __user *src, void *dst,
                                          int len, __wsum isum, int *errp);
-extern __wsum csum_partial_copy_to_user(const void *src, void __user *dst,
+extern __wsum csum_and_copy_to_user(const void *src, void __user *dst,
                                        int len, __wsum isum, int *errp);
 extern __wsum csum_partial_copy_nocheck(const void *src, void *dst,
                                        int len, __wsum sum);
 
-/* Old names. To be removed. */
-#define csum_and_copy_to_user csum_partial_copy_to_user
-#define csum_and_copy_from_user csum_partial_copy_from_user
-
 /**
  * ip_compute_csum - Compute an 16bit IP checksum.
  * @buff: buffer address.
index 52e9f3480f690336359bb8af713fcaf39bf3dfa1..d4edf281fff49db852fa9149e1b51249a49ae66d 100644 (file)
@@ -214,7 +214,11 @@ static inline bool in_compat_syscall(void)
 #endif
 
 struct compat_siginfo;
-int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
-               const kernel_siginfo_t *from, bool x32_ABI);
+
+#ifdef CONFIG_X86_X32_ABI
+int copy_siginfo_to_user32(struct compat_siginfo __user *to,
+               const kernel_siginfo_t *from);
+#define copy_siginfo_to_user32 copy_siginfo_to_user32
+#endif /* CONFIG_X86_X32_ABI */
 
 #endif /* _ASM_X86_COMPAT_H */
index cf3d621c68925a1558f25c37e3c90d04f5ef854e..eb8fcede9e3bf4365e3d5347b5ff27783b4a7c11 100644 (file)
 #define X86_CENTAUR_FAM6_C7_D          0xd
 #define X86_CENTAUR_FAM6_NANO          0xf
 
+#define X86_STEPPINGS(mins, maxs)    GENMASK(maxs, mins)
 /**
- * X86_MATCH_VENDOR_FAM_MODEL_FEATURE - Base macro for CPU matching
+ * X86_MATCH_VENDOR_FAM_MODEL_STEPPINGS_FEATURE - Base macro for CPU matching
  * @_vendor:   The vendor name, e.g. INTEL, AMD, HYGON, ..., ANY
  *             The name is expanded to X86_VENDOR_@_vendor
  * @_family:   The family number or X86_FAMILY_ANY
  * @_model:    The model number, model constant or X86_MODEL_ANY
+ * @_steppings:        Bitmask for steppings, stepping constant or X86_STEPPING_ANY
  * @_feature:  A X86_FEATURE bit or X86_FEATURE_ANY
  * @_data:     Driver specific data or NULL. The internal storage
  *             format is unsigned long. The supplied value, pointer
  * into another macro at the usage site for good reasons, then please
  * start this local macro with X86_MATCH to allow easy grepping.
  */
-#define X86_MATCH_VENDOR_FAM_MODEL_FEATURE(_vendor, _family, _model,   \
-                                          _feature, _data) {           \
+#define X86_MATCH_VENDOR_FAM_MODEL_STEPPINGS_FEATURE(_vendor, _family, _model, \
+                                                   _steppings, _feature, _data) { \
        .vendor         = X86_VENDOR_##_vendor,                         \
        .family         = _family,                                      \
        .model          = _model,                                       \
+       .steppings      = _steppings,                                   \
        .feature        = _feature,                                     \
        .driver_data    = (unsigned long) _data                         \
 }
 
+/**
+ * X86_MATCH_VENDOR_FAM_MODEL_FEATURE - Macro for CPU matching
+ * @_vendor:   The vendor name, e.g. INTEL, AMD, HYGON, ..., ANY
+ *             The name is expanded to X86_VENDOR_@_vendor
+ * @_family:   The family number or X86_FAMILY_ANY
+ * @_model:    The model number, model constant or X86_MODEL_ANY
+ * @_feature:  A X86_FEATURE bit or X86_FEATURE_ANY
+ * @_data:     Driver specific data or NULL. The internal storage
+ *             format is unsigned long. The supplied value, pointer
+ *             etc. is casted to unsigned long internally.
+ *
+ * The steppings arguments of X86_MATCH_VENDOR_FAM_MODEL_STEPPINGS_FEATURE() is
+ * set to wildcards.
+ */
+#define X86_MATCH_VENDOR_FAM_MODEL_FEATURE(vendor, family, model, feature, data) \
+       X86_MATCH_VENDOR_FAM_MODEL_STEPPINGS_FEATURE(vendor, family, model, \
+                                               X86_STEPPING_ANY, feature, data)
+
 /**
  * X86_MATCH_VENDOR_FAM_FEATURE - Macro for matching vendor, family and CPU feature
  * @vendor:    The vendor name, e.g. INTEL, AMD, HYGON, ..., ANY
 #define X86_MATCH_INTEL_FAM6_MODEL(model, data)                                \
        X86_MATCH_VENDOR_FAM_MODEL(INTEL, 6, INTEL_FAM6_##model, data)
 
+#define X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(model, steppings, data)   \
+       X86_MATCH_VENDOR_FAM_MODEL_STEPPINGS_FEATURE(INTEL, 6, INTEL_FAM6_##model, \
+                                                    steppings, X86_FEATURE_ANY, data)
+
 /*
  * Match specific microcode revisions.
  *
index 7e31f7f1bb062c8fdce18323ea308daf5c6ce647..49bd6cf3eec919d8896797b83d81b7600b6e456a 100644 (file)
@@ -3,7 +3,7 @@
 #define _ASM_X86_DEVICE_H
 
 struct dev_archdata {
-#if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
+#ifdef CONFIG_IOMMU_API
        void *iommu; /* hook for IOMMU specific extension */
 #endif
 };
index 00f7cf45e6999b8edb776784effcb6bcda778ddb..8e95aa4b0d172362263c178de77c7b324de39263 100644 (file)
@@ -74,7 +74,7 @@
 #define MAX_DMA_PFN   ((16UL * 1024 * 1024) >> PAGE_SHIFT)
 
 /* 4GB broken PCI/AGP hardware bus master zone */
-#define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT)
+#define MAX_DMA32_PFN (1UL << (32 - PAGE_SHIFT))
 
 #ifdef CONFIG_X86_32
 /* The maximum address that we can perform a DMA transfer to on this platform */
index af9a14ac896262a097707757e616b45c94ebc74a..54a6e4a2e132d1b9527a90793ba60ece450be07c 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef _ASM_X86_DOUBLEFAULT_H
 #define _ASM_X86_DOUBLEFAULT_H
 
-#if defined(CONFIG_X86_32) && defined(CONFIG_DOUBLEFAULT)
+#ifdef CONFIG_X86_32
 extern void doublefault_init_cpu_tss(void);
 #else
 static inline void doublefault_init_cpu_tss(void)
index 8391c115c0ecd32da2c9ad856bdb28caf3319143..89dcc7aa7e2cd6ed339f86a45cefffaa16bc4399 100644 (file)
@@ -9,6 +9,7 @@
 #include <asm/nospec-branch.h>
 #include <asm/mmu_context.h>
 #include <linux/build_bug.h>
+#include <linux/kernel.h>
 
 extern unsigned long efi_fw_vendor, efi_config_table;
 
@@ -225,14 +226,21 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size,
 
 /* arch specific definitions used by the stub code */
 
-__attribute_const__ bool efi_is_64bit(void);
+#ifdef CONFIG_EFI_MIXED
+
+#define ARCH_HAS_EFISTUB_WRAPPERS
+
+static inline bool efi_is_64bit(void)
+{
+       extern const bool efi_is64;
+
+       return efi_is64;
+}
 
 static inline bool efi_is_native(void)
 {
        if (!IS_ENABLED(CONFIG_X86_64))
                return true;
-       if (!IS_ENABLED(CONFIG_EFI_MIXED))
-               return true;
        return efi_is_64bit();
 }
 
@@ -286,6 +294,15 @@ static inline u32 efi64_convert_status(efi_status_t status)
 #define __efi64_argmap_allocate_pool(type, size, buffer)               \
        ((type), (size), efi64_zero_upper(buffer))
 
+#define __efi64_argmap_create_event(type, tpl, f, c, event)            \
+       ((type), (tpl), (f), (c), efi64_zero_upper(event))
+
+#define __efi64_argmap_set_timer(event, type, time)                    \
+       ((event), (type), lower_32_bits(time), upper_32_bits(time))
+
+#define __efi64_argmap_wait_for_event(num, event, index)               \
+       ((num), (event), efi64_zero_upper(index))
+
 #define __efi64_argmap_handle_protocol(handle, protocol, interface)    \
        ((handle), (protocol), efi64_zero_upper(interface))
 
@@ -307,6 +324,10 @@ static inline u32 efi64_convert_status(efi_status_t status)
 #define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \
        ((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf))
 
+/* Graphics Output Protocol */
+#define __efi64_argmap_query_mode(gop, mode, size, info)               \
+       ((gop), (mode), efi64_zero_upper(size), efi64_zero_upper(info))
+
 /*
  * The macros below handle the plumbing for the argument mapping. To add a
  * mapping for a specific EFI method, simply define a macro
@@ -335,15 +356,26 @@ static inline u32 efi64_convert_status(efi_status_t status)
 
 #define efi_bs_call(func, ...)                                         \
        (efi_is_native()                                                \
-               ? efi_system_table()->boottime->func(__VA_ARGS__)       \
-               : __efi64_thunk_map(efi_table_attr(efi_system_table(),  \
-                               boottime), func, __VA_ARGS__))
+               ? efi_system_table->boottime->func(__VA_ARGS__)         \
+               : __efi64_thunk_map(efi_table_attr(efi_system_table,    \
+                                                  boottime),           \
+                                   func, __VA_ARGS__))
 
 #define efi_rt_call(func, ...)                                         \
        (efi_is_native()                                                \
-               ? efi_system_table()->runtime->func(__VA_ARGS__)        \
-               : __efi64_thunk_map(efi_table_attr(efi_system_table(),  \
-                               runtime), func, __VA_ARGS__))
+               ? efi_system_table->runtime->func(__VA_ARGS__)          \
+               : __efi64_thunk_map(efi_table_attr(efi_system_table,    \
+                                                  runtime),            \
+                                   func, __VA_ARGS__))
+
+#else /* CONFIG_EFI_MIXED */
+
+static inline bool efi_is_64bit(void)
+{
+       return IS_ENABLED(CONFIG_X86_64);
+}
+
+#endif /* CONFIG_EFI_MIXED */
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
index 44c48e34d7994f885a12e67ae8d311f82fce7761..42159f45bf9c429e1f726943fc6cd61ed2f64b55 100644 (file)
@@ -31,7 +31,8 @@ extern void fpu__save(struct fpu *fpu);
 extern int  fpu__restore_sig(void __user *buf, int ia32_frame);
 extern void fpu__drop(struct fpu *fpu);
 extern int  fpu__copy(struct task_struct *dst, struct task_struct *src);
-extern void fpu__clear(struct fpu *fpu);
+extern void fpu__clear_user_states(struct fpu *fpu);
+extern void fpu__clear_all(struct fpu *fpu);
 extern int  fpu__exception_code(struct fpu *fpu, int trap_nr);
 extern int  dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate);
 
@@ -92,7 +93,7 @@ static inline void fpstate_init_xstate(struct xregs_state *xsave)
         * XRSTORS requires these bits set in xcomp_bv, or it will
         * trigger #GP:
         */
-       xsave->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT | xfeatures_mask;
+       xsave->header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT | xfeatures_mask_all;
 }
 
 static inline void fpstate_init_fxstate(struct fxregs_state *fx)
@@ -399,7 +400,10 @@ static inline int copy_kernel_to_xregs_err(struct xregs_state *xstate, u64 mask)
        u32 hmask = mask >> 32;
        int err;
 
-       XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
+       if (static_cpu_has(X86_FEATURE_XSAVES))
+               XSTATE_OP(XRSTORS, xstate, lmask, hmask, err);
+       else
+               XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
 
        return err;
 }
index c6136d79f8c07f90148a22ed3d8bae7a1e0d0b46..422d8369012a988685ec65350b42c22cc78ddfb4 100644 (file)
 #define XSAVE_YMM_SIZE     256
 #define XSAVE_YMM_OFFSET    (XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET)
 
-/* Supervisor features */
-#define XFEATURE_MASK_SUPERVISOR (XFEATURE_MASK_PT)
-
-/* All currently supported features */
-#define XCNTXT_MASK            (XFEATURE_MASK_FP | \
-                                XFEATURE_MASK_SSE | \
-                                XFEATURE_MASK_YMM | \
-                                XFEATURE_MASK_OPMASK | \
-                                XFEATURE_MASK_ZMM_Hi256 | \
-                                XFEATURE_MASK_Hi16_ZMM  | \
-                                XFEATURE_MASK_PKRU | \
-                                XFEATURE_MASK_BNDREGS | \
-                                XFEATURE_MASK_BNDCSR)
+/* All currently supported user features */
+#define XFEATURE_MASK_USER_SUPPORTED (XFEATURE_MASK_FP | \
+                                     XFEATURE_MASK_SSE | \
+                                     XFEATURE_MASK_YMM | \
+                                     XFEATURE_MASK_OPMASK | \
+                                     XFEATURE_MASK_ZMM_Hi256 | \
+                                     XFEATURE_MASK_Hi16_ZMM     | \
+                                     XFEATURE_MASK_PKRU | \
+                                     XFEATURE_MASK_BNDREGS | \
+                                     XFEATURE_MASK_BNDCSR)
+
+/* All currently supported supervisor features */
+#define XFEATURE_MASK_SUPERVISOR_SUPPORTED (0)
+
+/*
+ * Unsupported supervisor features. When a supervisor feature in this mask is
+ * supported in the future, move it to the supported supervisor feature mask.
+ */
+#define XFEATURE_MASK_SUPERVISOR_UNSUPPORTED (XFEATURE_MASK_PT)
+
+/* All supervisor states including supported and unsupported states. */
+#define XFEATURE_MASK_SUPERVISOR_ALL (XFEATURE_MASK_SUPERVISOR_SUPPORTED | \
+                                     XFEATURE_MASK_SUPERVISOR_UNSUPPORTED)
 
 #ifdef CONFIG_X86_64
 #define REX_PREFIX     "0x48, "
 #define REX_PREFIX
 #endif
 
-extern u64 xfeatures_mask;
+extern u64 xfeatures_mask_all;
+
+static inline u64 xfeatures_mask_supervisor(void)
+{
+       return xfeatures_mask_all & XFEATURE_MASK_SUPERVISOR_SUPPORTED;
+}
+
+static inline u64 xfeatures_mask_user(void)
+{
+       return xfeatures_mask_all & XFEATURE_MASK_USER_SUPPORTED;
+}
+
 extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
 
 extern void __init update_regset_xstate_info(unsigned int size,
@@ -54,8 +75,9 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of
 int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned int offset, unsigned int size);
 int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf);
 int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf);
+void copy_supervisor_to_kernel(struct xregs_state *xsave);
 
 /* Validate an xstate header supplied by userspace (ptrace or sigreturn) */
-extern int validate_xstate_header(const struct xstate_header *hdr);
+int validate_user_xstate_header(const struct xstate_header *hdr);
 
 #endif
index 8e5af119dc2d8c2722611ee3250b2c2f598a9661..de58391bdee062b1fa548f10689052019a4fd096 100644 (file)
@@ -88,11 +88,17 @@ static inline bool intel_mid_has_msic(void)
        return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL);
 }
 
+extern void intel_scu_devices_create(void);
+extern void intel_scu_devices_destroy(void);
+
 #else /* !CONFIG_X86_INTEL_MID */
 
 #define intel_mid_identify_cpu()       0
 #define intel_mid_has_msic()           0
 
+static inline void intel_scu_devices_create(void) { }
+static inline void intel_scu_devices_destroy(void) { }
+
 #endif /* !CONFIG_X86_INTEL_MID */
 
 enum intel_mid_timer_options {
@@ -115,9 +121,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
 #define SFI_MTMR_MAX_NUM               8
 #define SFI_MRTC_MAX                   8
 
-extern void intel_scu_devices_create(void);
-extern void intel_scu_devices_destroy(void);
-
 /* VRTC timer */
 #define MRST_VRTC_MAP_SZ               1024
 /* #define MRST_VRTC_PGOFFSET          0xc00 */
diff --git a/arch/x86/include/asm/intel_pmc_ipc.h b/arch/x86/include/asm/intel_pmc_ipc.h
deleted file mode 100644 (file)
index e6da1ce..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_X86_INTEL_PMC_IPC_H_
-#define  _ASM_X86_INTEL_PMC_IPC_H_
-
-/* Commands */
-#define PMC_IPC_PMIC_ACCESS            0xFF
-#define                PMC_IPC_PMIC_ACCESS_READ        0x0
-#define                PMC_IPC_PMIC_ACCESS_WRITE       0x1
-#define PMC_IPC_USB_PWR_CTRL           0xF0
-#define PMC_IPC_PMIC_BLACKLIST_SEL     0xEF
-#define PMC_IPC_PHY_CONFIG             0xEE
-#define PMC_IPC_NORTHPEAK_CTRL         0xED
-#define PMC_IPC_PM_DEBUG               0xEC
-#define PMC_IPC_PMC_TELEMTRY           0xEB
-#define PMC_IPC_PMC_FW_MSG_CTRL                0xEA
-
-/* IPC return code */
-#define IPC_ERR_NONE                   0
-#define IPC_ERR_CMD_NOT_SUPPORTED      1
-#define IPC_ERR_CMD_NOT_SERVICED       2
-#define IPC_ERR_UNABLE_TO_SERVICE      3
-#define IPC_ERR_CMD_INVALID            4
-#define IPC_ERR_CMD_FAILED             5
-#define IPC_ERR_EMSECURITY             6
-#define IPC_ERR_UNSIGNEDKERNEL         7
-
-/* GCR reg offsets from gcr base*/
-#define PMC_GCR_PMC_CFG_REG            0x08
-#define PMC_GCR_TELEM_DEEP_S0IX_REG    0x78
-#define PMC_GCR_TELEM_SHLW_S0IX_REG    0x80
-
-#if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
-
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-               u32 *out, u32 outlen);
-int intel_pmc_s0ix_counter_read(u64 *data);
-int intel_pmc_gcr_read64(u32 offset, u64 *data);
-
-#else
-
-static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-               u32 *out, u32 outlen)
-{
-       return -EINVAL;
-}
-
-static inline int intel_pmc_s0ix_counter_read(u64 *data)
-{
-       return -EINVAL;
-}
-
-static inline int intel_pmc_gcr_read64(u32 offset, u64 *data)
-{
-       return -EINVAL;
-}
-
-#endif /*CONFIG_INTEL_PMC_IPC*/
-
-#endif
index 2a1442ba6e78146975824b2e66968f5424db3b44..11d457af68c5420bb0bf84149ac1fdd7e4f91611 100644 (file)
@@ -2,61 +2,69 @@
 #ifndef _ASM_X86_INTEL_SCU_IPC_H_
 #define  _ASM_X86_INTEL_SCU_IPC_H_
 
-#include <linux/notifier.h>
-
-#define IPCMSG_INDIRECT_READ   0x02
-#define IPCMSG_INDIRECT_WRITE  0x05
-
-#define IPCMSG_COLD_OFF                0x80    /* Only for Tangier */
-
-#define IPCMSG_WARM_RESET      0xF0
-#define IPCMSG_COLD_RESET      0xF1
-#define IPCMSG_SOFT_RESET      0xF2
-#define IPCMSG_COLD_BOOT       0xF3
-
-#define IPCMSG_VRTC            0xFA     /* Set vRTC device */
-       /* Command id associated with message IPCMSG_VRTC */
-       #define IPC_CMD_VRTC_SETTIME      1 /* Set time */
-       #define IPC_CMD_VRTC_SETALARM     2 /* Set alarm */
-
-/* Read single register */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data);
-
-/* Read a vector */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
-
-/* Write single register */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data);
-
-/* Write a vector */
-int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
-
-/* Update single register based on the mask */
-int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
-
-/* Issue commands to the SCU with or without data */
-int intel_scu_ipc_simple_command(int cmd, int sub);
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-                         u32 *out, int outlen);
-
-extern struct blocking_notifier_head intel_scu_notifier;
-
-static inline void intel_scu_notifier_add(struct notifier_block *nb)
-{
-       blocking_notifier_chain_register(&intel_scu_notifier, nb);
-}
-
-static inline void intel_scu_notifier_remove(struct notifier_block *nb)
-{
-       blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
-}
-
-static inline int intel_scu_notifier_post(unsigned long v, void *p)
+#include <linux/ioport.h>
+
+struct device;
+struct intel_scu_ipc_dev;
+
+/**
+ * struct intel_scu_ipc_data - Data used to configure SCU IPC
+ * @mem: Base address of SCU IPC MMIO registers
+ * @irq: The IRQ number used for SCU (optional)
+ */
+struct intel_scu_ipc_data {
+       struct resource mem;
+       int irq;
+};
+
+struct intel_scu_ipc_dev *
+__intel_scu_ipc_register(struct device *parent,
+                        const struct intel_scu_ipc_data *scu_data,
+                        struct module *owner);
+
+#define intel_scu_ipc_register(parent, scu_data)  \
+       __intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
+
+void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
+
+struct intel_scu_ipc_dev *
+__devm_intel_scu_ipc_register(struct device *parent,
+                             const struct intel_scu_ipc_data *scu_data,
+                             struct module *owner);
+
+#define devm_intel_scu_ipc_register(parent, scu_data)  \
+       __devm_intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
+
+struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
+void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
+struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
+
+int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
+                             u8 *data);
+int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr,
+                              u8 data);
+int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr,
+                           u8 *data, size_t len);
+int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr,
+                            u8 *data, size_t len);
+
+int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
+                            u8 data, u8 mask);
+
+int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
+                                    int sub);
+int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
+                                       int sub, const void *in, size_t inlen,
+                                       size_t size, void *out, size_t outlen);
+
+static inline int intel_scu_ipc_dev_command(struct intel_scu_ipc_dev *scu, int cmd,
+                                           int sub, const void *in, size_t inlen,
+                                           void *out, size_t outlen)
 {
-       return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
+       return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
+                                                  inlen, out, outlen);
 }
 
-#define                SCU_AVAILABLE           1
-#define                SCU_DOWN                2
+#include <asm/intel_scu_ipc_legacy.h>
 
 #endif
diff --git a/arch/x86/include/asm/intel_scu_ipc_legacy.h b/arch/x86/include/asm/intel_scu_ipc_legacy.h
new file mode 100644 (file)
index 0000000..4cf13fe
--- /dev/null
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
+#define _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
+
+#include <linux/notifier.h>
+
+#define IPCMSG_INDIRECT_READ   0x02
+#define IPCMSG_INDIRECT_WRITE  0x05
+
+#define IPCMSG_COLD_OFF                0x80    /* Only for Tangier */
+
+#define IPCMSG_WARM_RESET      0xF0
+#define IPCMSG_COLD_RESET      0xF1
+#define IPCMSG_SOFT_RESET      0xF2
+#define IPCMSG_COLD_BOOT       0xF3
+
+#define IPCMSG_VRTC            0xFA    /* Set vRTC device */
+/* Command id associated with message IPCMSG_VRTC */
+#define IPC_CMD_VRTC_SETTIME      1    /* Set time */
+#define IPC_CMD_VRTC_SETALARM     2    /* Set alarm */
+
+/* Don't call these in new code - they will be removed eventually */
+
+/* Read single register */
+static inline int intel_scu_ipc_ioread8(u16 addr, u8 *data)
+{
+       return intel_scu_ipc_dev_ioread8(NULL, addr, data);
+}
+
+/* Read a vector */
+static inline int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
+{
+       return intel_scu_ipc_dev_readv(NULL, addr, data, len);
+}
+
+/* Write single register */
+static inline int intel_scu_ipc_iowrite8(u16 addr, u8 data)
+{
+       return intel_scu_ipc_dev_iowrite8(NULL, addr, data);
+}
+
+/* Write a vector */
+static inline int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
+{
+       return intel_scu_ipc_dev_writev(NULL, addr, data, len);
+}
+
+/* Update single register based on the mask */
+static inline int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask)
+{
+       return intel_scu_ipc_dev_update(NULL, addr, data, mask);
+}
+
+/* Issue commands to the SCU with or without data */
+static inline int intel_scu_ipc_simple_command(int cmd, int sub)
+{
+       return intel_scu_ipc_dev_simple_command(NULL, cmd, sub);
+}
+
+static inline int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
+                                       u32 *out, int outlen)
+{
+       /* New API takes both inlen and outlen as bytes so convert here */
+       size_t inbytes = inlen * sizeof(u32);
+       size_t outbytes = outlen * sizeof(u32);
+
+       return intel_scu_ipc_dev_command_with_size(NULL, cmd, sub, in, inbytes,
+                                                  inlen, out, outbytes);
+}
+
+extern struct blocking_notifier_head intel_scu_notifier;
+
+static inline void intel_scu_notifier_add(struct notifier_block *nb)
+{
+       blocking_notifier_chain_register(&intel_scu_notifier, nb);
+}
+
+static inline void intel_scu_notifier_remove(struct notifier_block *nb)
+{
+       blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
+}
+
+static inline int intel_scu_notifier_post(unsigned long v, void *p)
+{
+       return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
+}
+
+#define                SCU_AVAILABLE           1
+#define                SCU_DOWN                2
+
+#endif
index 2f77e31a1283c733a09e95db3f3fa3057281ae65..8046e70dfd7cf7a96314b50e6a86c7bff6f1ebc2 100644 (file)
@@ -10,6 +10,8 @@
 #define TELEM_MAX_EVENTS_SRAM          28
 #define TELEM_MAX_OS_ALLOCATED_EVENTS  20
 
+#include <asm/intel_scu_ipc.h>
+
 enum telemetry_unit {
        TELEM_PSS = 0,
        TELEM_IOSS,
@@ -51,6 +53,8 @@ struct telemetry_plt_config {
        struct telemetry_unit_config ioss_config;
        struct mutex telem_trace_lock;
        struct mutex telem_lock;
+       struct intel_pmc_dev *pmc;
+       struct intel_scu_ipc_dev *scu;
        bool telem_in_use;
 };
 
@@ -92,7 +96,7 @@ int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
 
 int telemetry_clear_pltdata(void);
 
-int telemetry_pltconfig_valid(void);
+struct telemetry_plt_config *telemetry_get_pltdata(void);
 
 int telemetry_get_evtname(enum telemetry_unit telem_unit,
                          const char **name, int len);
index 989cfa86de85184359e0205de9237f9e048e5e20..734482afbf81d30eb008a1a36593710637e9be0e 100644 (file)
@@ -12,12 +12,9 @@ static inline void __invpcid(unsigned long pcid, unsigned long addr,
         * stale TLB entries and, especially if we're flushing global
         * mappings, we don't want the compiler to reorder any subsequent
         * memory accesses before the TLB flush.
-        *
-        * The hex opcode is invpcid (%ecx), %eax in 32-bit mode and
-        * invpcid (%rcx), %rax in long mode.
         */
-       asm volatile (".byte 0x66, 0x0f, 0x38, 0x82, 0x01"
-                     : : "m" (desc), "a" (type), "c" (&desc) : "memory");
+       asm volatile("invpcid %[desc], %[type]"
+                    :: [desc] "m" (desc), [type] "r" (type) : "memory");
 }
 
 #define INVPCID_TYPE_INDIV_ADDR                0
index 07344d82e88ee6b28e4bb2040932eb1135a3b4a3..ac1a99ffbd8d263e4dc2e8a6cba3b589a9913d58 100644 (file)
@@ -17,7 +17,7 @@ struct task_struct;
 
 #ifdef CONFIG_X86_IOPL_IOPERM
 void io_bitmap_share(struct task_struct *tsk);
-void io_bitmap_exit(void);
+void io_bitmap_exit(struct task_struct *tsk);
 
 void native_tss_update_io_bitmap(void);
 
@@ -29,7 +29,7 @@ void native_tss_update_io_bitmap(void);
 
 #else
 static inline void io_bitmap_share(struct task_struct *tsk) { }
-static inline void io_bitmap_exit(void) { }
+static inline void io_bitmap_exit(struct task_struct *tsk) { }
 static inline void tss_update_io_bitmap(void) { }
 #endif
 
index 0a6b35353fc794c7ef6dcb4fbc38915221f1ed79..e94b3de564d6a345f168460e5f4bdaebcef34fa9 100644 (file)
@@ -1279,8 +1279,7 @@ extern struct kmem_cache *x86_fpu_cache;
 #define __KVM_HAVE_ARCH_VM_ALLOC
 static inline struct kvm *kvm_arch_alloc_vm(void)
 {
-       return __vmalloc(kvm_x86_ops.vm_size,
-                        GFP_KERNEL_ACCOUNT | __GFP_ZERO, PAGE_KERNEL);
+       return __vmalloc(kvm_x86_ops.vm_size, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
 }
 void kvm_arch_free_vm(struct kvm *kvm);
 
index 73d8dd14dda268ee2ae9a4ab6f56f493e7cf4c04..2d4515e8b7df88a9d396b27fd6bcaf9187e489d4 100644 (file)
@@ -14,43 +14,4 @@ extern struct pglist_data *node_data[];
 #define NODE_DATA(nid) (node_data[nid])
 #endif /* CONFIG_NUMA */
 
-#ifdef CONFIG_DISCONTIGMEM
-
-/*
- * generic node memory support, the following assumptions apply:
- *
- * 1) memory comes in 64Mb contiguous chunks which are either present or not
- * 2) we will not have more than 64Gb in total
- *
- * for now assume that 64Gb is max amount of RAM for whole system
- *    64Gb / 4096bytes/page = 16777216 pages
- */
-#define MAX_NR_PAGES 16777216
-#define MAX_SECTIONS 1024
-#define PAGES_PER_SECTION (MAX_NR_PAGES/MAX_SECTIONS)
-
-extern s8 physnode_map[];
-
-static inline int pfn_to_nid(unsigned long pfn)
-{
-#ifdef CONFIG_NUMA
-       return((int) physnode_map[(pfn) / PAGES_PER_SECTION]);
-#else
-       return 0;
-#endif
-}
-
-static inline int pfn_valid(int pfn)
-{
-       int nid = pfn_to_nid(pfn);
-
-       if (nid >= 0)
-               return (pfn < node_end_pfn(nid));
-       return 0;
-}
-
-#define early_pfn_valid(pfn)   pfn_valid((pfn))
-
-#endif /* CONFIG_DISCONTIGMEM */
-
 #endif /* _ASM_X86_MMZONE_32_H */
index 12c9684d59ba6fea4df36257c7f1b8eb89ab5943..ef452b817f44f31fa979ce25701a86f94c5e2a77 100644 (file)
 #define MSR_PP1_ENERGY_STATUS          0x00000641
 #define MSR_PP1_POLICY                 0x00000642
 
+#define MSR_AMD_PKG_ENERGY_STATUS      0xc001029b
+#define MSR_AMD_RAPL_POWER_UNIT                0xc0010299
+
 /* Config TDP MSRs */
 #define MSR_CONFIG_TDP_NOMINAL         0x00000648
 #define MSR_CONFIG_TDP_LEVEL_1         0x00000649
index 7e9a281e266049b06668ec6f659f37f03f1052e9..d52d1aacdd971c416f54071200d11c379ba5d83a 100644 (file)
@@ -4,20 +4,13 @@
 #define _ASM_X86_NOSPEC_BRANCH_H_
 
 #include <linux/static_key.h>
+#include <linux/frame.h>
 
 #include <asm/alternative.h>
 #include <asm/alternative-asm.h>
 #include <asm/cpufeatures.h>
 #include <asm/msr-index.h>
-
-/*
- * This should be used immediately before a retpoline alternative. It tells
- * objtool where the retpolines are so that it can make sense of the control
- * flow by just reading the original instruction(s) and ignoring the
- * alternatives.
- */
-#define ANNOTATE_NOSPEC_ALTERNATIVE \
-       ANNOTATE_IGNORE_ALTERNATIVE
+#include <asm/unwind_hints.h>
 
 /*
  * Fill the CPU return stack buffer.
 #define __FILL_RETURN_BUFFER(reg, nr, sp)      \
        mov     $(nr/2), reg;                   \
 771:                                           \
+       ANNOTATE_INTRA_FUNCTION_CALL;           \
        call    772f;                           \
 773:   /* speculation trap */                  \
+       UNWIND_HINT_EMPTY;                      \
        pause;                                  \
        lfence;                                 \
        jmp     773b;                           \
 772:                                           \
+       ANNOTATE_INTRA_FUNCTION_CALL;           \
        call    774f;                           \
 775:   /* speculation trap */                  \
+       UNWIND_HINT_EMPTY;                      \
        pause;                                  \
        lfence;                                 \
        jmp     775b;                           \
 774:                                           \
+       add     $(BITS_PER_LONG/8) * 2, sp;     \
        dec     reg;                            \
-       jnz     771b;                           \
-       add     $(BITS_PER_LONG/8) * nr, sp;
+       jnz     771b;
 
 #ifdef __ASSEMBLY__
 
        .popsection
 .endm
 
-/*
- * These are the bare retpoline primitives for indirect jmp and call.
- * Do not use these directly; they only exist to make the ALTERNATIVE
- * invocation below less ugly.
- */
-.macro RETPOLINE_JMP reg:req
-       call    .Ldo_rop_\@
-.Lspec_trap_\@:
-       pause
-       lfence
-       jmp     .Lspec_trap_\@
-.Ldo_rop_\@:
-       mov     \reg, (%_ASM_SP)
-       ret
-.endm
-
-/*
- * This is a wrapper around RETPOLINE_JMP so the called function in reg
- * returns to the instruction after the macro.
- */
-.macro RETPOLINE_CALL reg:req
-       jmp     .Ldo_call_\@
-.Ldo_retpoline_jmp_\@:
-       RETPOLINE_JMP \reg
-.Ldo_call_\@:
-       call    .Ldo_retpoline_jmp_\@
-.endm
-
 /*
  * JMP_NOSPEC and CALL_NOSPEC macros can be used instead of a simple
  * indirect jmp/call which may be susceptible to the Spectre variant 2
  */
 .macro JMP_NOSPEC reg:req
 #ifdef CONFIG_RETPOLINE
-       ANNOTATE_NOSPEC_ALTERNATIVE
-       ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *\reg),  \
-               __stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
-               __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
+       ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), \
+                     __stringify(jmp __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
+                     __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), X86_FEATURE_RETPOLINE_AMD
 #else
-       jmp     *\reg
+       jmp     *%\reg
 #endif
 .endm
 
 .macro CALL_NOSPEC reg:req
 #ifdef CONFIG_RETPOLINE
-       ANNOTATE_NOSPEC_ALTERNATIVE
-       ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *\reg), \
-               __stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
-               __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *\reg), X86_FEATURE_RETPOLINE_AMD
+       ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *%\reg), \
+                     __stringify(call __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
+                     __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *%\reg), X86_FEATURE_RETPOLINE_AMD
 #else
-       call    *\reg
+       call    *%\reg
 #endif
 .endm
 
   */
 .macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
 #ifdef CONFIG_RETPOLINE
-       ANNOTATE_NOSPEC_ALTERNATIVE
-       ALTERNATIVE "jmp .Lskip_rsb_\@",                                \
-               __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP))    \
-               \ftr
+       ALTERNATIVE "jmp .Lskip_rsb_\@", "", \ftr
+       __FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)
 .Lskip_rsb_\@:
 #endif
 .endm
  * which is ensured when CONFIG_RETPOLINE is defined.
  */
 # define CALL_NOSPEC                                           \
-       ANNOTATE_NOSPEC_ALTERNATIVE                             \
        ALTERNATIVE_2(                                          \
        ANNOTATE_RETPOLINE_SAFE                                 \
        "call *%[thunk_target]\n",                              \
-       "call __x86_indirect_thunk_%V[thunk_target]\n",         \
+       "call __x86_retpoline_%V[thunk_target]\n",              \
        X86_FEATURE_RETPOLINE,                                  \
        "lfence;\n"                                             \
        ANNOTATE_RETPOLINE_SAFE                                 \
        "call *%[thunk_target]\n",                              \
        X86_FEATURE_RETPOLINE_AMD)
+
 # define THUNK_TARGET(addr) [thunk_target] "r" (addr)
 
 #else /* CONFIG_X86_32 */
  * here, anyway.
  */
 # define CALL_NOSPEC                                           \
-       ANNOTATE_NOSPEC_ALTERNATIVE                             \
        ALTERNATIVE_2(                                          \
        ANNOTATE_RETPOLINE_SAFE                                 \
        "call *%[thunk_target]\n",                              \
index 6e060907c163bfdf98e1e200d731a2c181c08732..d25534940bde04f38796e8e36a57626b81f02d03 100644 (file)
@@ -58,8 +58,7 @@
 #define ORC_TYPE_CALL                  0
 #define ORC_TYPE_REGS                  1
 #define ORC_TYPE_REGS_IRET             2
-#define UNWIND_HINT_TYPE_SAVE          3
-#define UNWIND_HINT_TYPE_RESTORE       4
+#define UNWIND_HINT_TYPE_RET_OFFSET    3
 
 #ifndef __ASSEMBLY__
 /*
index 6deb6cd236e3901c59a3b5ab7e1db0f4f6471548..7f6ccff0ba727c94155d86ccc612ad7609de0317 100644 (file)
@@ -20,6 +20,8 @@ typedef union {
 
 #define SHARED_KERNEL_PMD      0
 
+#define ARCH_PAGE_TABLE_SYNC_MASK      PGTBL_PMD_MODIFIED
+
 /*
  * traditional i386 two-level paging structure:
  */
index 33845d36897c843480f20e12afe224e3463e3666..80fbb4a9ed87b5bd84617bed90391e28ffba4670 100644 (file)
@@ -27,6 +27,8 @@ typedef union {
 #define SHARED_KERNEL_PMD      (!static_cpu_has(X86_FEATURE_PTI))
 #endif
 
+#define ARCH_PAGE_TABLE_SYNC_MASK      (SHARED_KERNEL_PMD ? 0 : PGTBL_PMD_MODIFIED)
+
 /*
  * PGDIR_SHIFT determines what a top-level page table entry can map
  */
index 0dca7f7aeff2baa141cba616446227909b60d76f..be7b196468971375e85da5f9fac723ad5a83473b 100644 (file)
@@ -66,8 +66,7 @@ do {                                          \
 #endif /* !__ASSEMBLY__ */
 
 /*
- * kern_addr_valid() is (1) for FLATMEM and (0) for
- * SPARSEMEM and DISCONTIGMEM
+ * kern_addr_valid() is (1) for FLATMEM and (0) for SPARSEMEM
  */
 #ifdef CONFIG_FLATMEM
 #define kern_addr_valid(addr)  (1)
index 52e5f5f2240d9cb7bfe150e8fc723684b5c43617..8f63efb2a2ccf461cad4387b3ff22f00acdc602d 100644 (file)
@@ -159,4 +159,6 @@ extern unsigned int ptrs_per_p4d;
 
 #define PGD_KERNEL_START       ((PAGE_SIZE / 2) / sizeof(pgd_t))
 
+#define ARCH_PAGE_TABLE_SYNC_MASK      (pgtable_l5_enabled() ? PGTBL_PGD_MODIFIED : PGTBL_P4D_MODIFIED)
+
 #endif /* _ASM_X86_PGTABLE_64_DEFS_H */
index b6606fe6cfdf363a12f76e3e8aef16e52fabba4f..2e7c442cc618e35ab532bafaf8431cd32b3bb326 100644 (file)
@@ -194,7 +194,6 @@ enum page_cache_mode {
 #define _PAGE_TABLE_NOENC       (__PP|__RW|_USR|___A|   0|___D|   0|   0)
 #define _PAGE_TABLE             (__PP|__RW|_USR|___A|   0|___D|   0|   0| _ENC)
 #define __PAGE_KERNEL_RO        (__PP|   0|   0|___A|__NX|___D|   0|___G)
-#define __PAGE_KERNEL_RX        (__PP|   0|   0|___A|   0|___D|   0|___G)
 #define __PAGE_KERNEL_NOCACHE   (__PP|__RW|   0|___A|__NX|___D|   0|___G| __NC)
 #define __PAGE_KERNEL_VVAR      (__PP|   0|_USR|___A|__NX|___D|   0|___G)
 #define __PAGE_KERNEL_LARGE     (__PP|__RW|   0|___A|__NX|___D|_PSE|___G)
@@ -220,7 +219,6 @@ enum page_cache_mode {
 #define PAGE_KERNEL_RO         __pgprot_mask(__PAGE_KERNEL_RO         | _ENC)
 #define PAGE_KERNEL_EXEC       __pgprot_mask(__PAGE_KERNEL_EXEC       | _ENC)
 #define PAGE_KERNEL_EXEC_NOENC __pgprot_mask(__PAGE_KERNEL_EXEC       |    0)
-#define PAGE_KERNEL_RX         __pgprot_mask(__PAGE_KERNEL_RX         | _ENC)
 #define PAGE_KERNEL_NOCACHE    __pgprot_mask(__PAGE_KERNEL_NOCACHE    | _ENC)
 #define PAGE_KERNEL_LARGE      __pgprot_mask(__PAGE_KERNEL_LARGE      | _ENC)
 #define PAGE_KERNEL_LARGE_EXEC __pgprot_mask(__PAGE_KERNEL_LARGE_EXEC | _ENC)
@@ -284,6 +282,12 @@ typedef struct pgprot { pgprotval_t pgprot; } pgprot_t;
 
 typedef struct { pgdval_t pgd; } pgd_t;
 
+static inline pgprot_t pgprot_nx(pgprot_t prot)
+{
+       return __pgprot(pgprot_val(prot) | _PAGE_NX);
+}
+#define pgprot_nx pgprot_nx
+
 #ifdef CONFIG_X86_PAE
 
 /*
index 3bcf27caf6c9f740d3ecfb1f82f797d1e65fa170..29ee0c088009d026d0059f2a983283eb332e5ec5 100644 (file)
@@ -113,9 +113,10 @@ struct cpuinfo_x86 {
        /* in KB - valid for CPUS which support this call: */
        unsigned int            x86_cache_size;
        int                     x86_cache_alignment;    /* In bytes */
-       /* Cache QoS architectural values: */
+       /* Cache QoS architectural values, valid only on the BSP: */
        int                     x86_cache_max_rmid;     /* max index */
        int                     x86_cache_occ_scale;    /* scale to bytes */
+       int                     x86_cache_mbm_width_offset;
        int                     x86_power;
        unsigned long           loops_per_jiffy;
        /* cpuid returned max cores value: */
@@ -727,7 +728,6 @@ static inline void sync_core(void)
        unsigned int tmp;
 
        asm volatile (
-               UNWIND_HINT_SAVE
                "mov %%ss, %0\n\t"
                "pushq %q0\n\t"
                "pushq %%rsp\n\t"
@@ -737,7 +737,6 @@ static inline void sync_core(void)
                "pushq %q0\n\t"
                "pushq $1f\n\t"
                "iretq\n\t"
-               UNWIND_HINT_RESTORE
                "1:"
                : "=&r" (tmp), ASM_CALL_CONSTRAINT : : "cc", "memory");
 #endif
similarity index 92%
rename from arch/x86/include/asm/resctrl_sched.h
rename to arch/x86/include/asm/resctrl.h
index f6b7fe2833cc72a78bed2c0cad3b59b8c6eb4f23..07603064df8fcf5b5f25c50fcf5655f6bb0461e5 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_X86_RESCTRL_SCHED_H
-#define _ASM_X86_RESCTRL_SCHED_H
+#ifndef _ASM_X86_RESCTRL_H
+#define _ASM_X86_RESCTRL_H
 
 #ifdef CONFIG_X86_CPU_RESCTRL
 
@@ -84,10 +84,13 @@ static inline void resctrl_sched_in(void)
                __resctrl_sched_in();
 }
 
+void resctrl_cpu_detect(struct cpuinfo_x86 *c);
+
 #else
 
 static inline void resctrl_sched_in(void) {}
+static inline void resctrl_cpu_detect(struct cpuinfo_x86 *c) {}
 
 #endif /* CONFIG_X86_CPU_RESCTRL */
 
-#endif /* _ASM_X86_RESCTRL_SCHED_H */
+#endif /* _ASM_X86_RESCTRL_H */
index 27c47d183f4b3760874549df7eb7cb1acb73fe0e..8b58d6975d5d4b4fa3a77e25529caf464b23bbfe 100644 (file)
@@ -57,8 +57,10 @@ static __always_inline unsigned long smap_save(void)
 {
        unsigned long flags;
 
-       asm volatile (ALTERNATIVE("", "pushf; pop %0; " __ASM_CLAC,
-                                 X86_FEATURE_SMAP)
+       asm volatile ("# smap_save\n\t"
+                     ALTERNATIVE("jmp 1f", "", X86_FEATURE_SMAP)
+                     "pushf; pop %0; " __ASM_CLAC "\n\t"
+                     "1:"
                      : "=rm" (flags) : : "memory", "cc");
 
        return flags;
@@ -66,7 +68,10 @@ static __always_inline unsigned long smap_save(void)
 
 static __always_inline void smap_restore(unsigned long flags)
 {
-       asm volatile (ALTERNATIVE("", "push %0; popf", X86_FEATURE_SMAP)
+       asm volatile ("# smap_restore\n\t"
+                     ALTERNATIVE("jmp 1f", "", X86_FEATURE_SMAP)
+                     "push %0; popf\n\t"
+                     "1:"
                      : : "g" (flags) : "memory", "cc");
 }
 
index bf3e34b25afc82cedc116c2c25bdf2bd218bbc36..323db6c5852a2aa0c5c4a4e52e784bc5af5546a5 100644 (file)
@@ -3,29 +3,7 @@
 #define _ASM_X86_SPINLOCK_TYPES_H
 
 #include <linux/types.h>
-
-#ifdef CONFIG_PARAVIRT_SPINLOCKS
-#define __TICKET_LOCK_INC      2
-#define TICKET_SLOWPATH_FLAG   ((__ticket_t)1)
-#else
-#define __TICKET_LOCK_INC      1
-#define TICKET_SLOWPATH_FLAG   ((__ticket_t)0)
-#endif
-
-#if (CONFIG_NR_CPUS < (256 / __TICKET_LOCK_INC))
-typedef u8  __ticket_t;
-typedef u16 __ticketpair_t;
-#else
-typedef u16 __ticket_t;
-typedef u32 __ticketpair_t;
-#endif
-
-#define TICKET_LOCK_INC        ((__ticket_t)__TICKET_LOCK_INC)
-
-#define TICKET_SHIFT   (sizeof(__ticket_t) * 8)
-
 #include <asm-generic/qspinlock_types.h>
-
 #include <asm-generic/qrwlock_types.h>
 
 #endif /* _ASM_X86_SPINLOCK_TYPES_H */
index 0e059b73437b4ca58cfc6b7ee833b9fb219b5e39..9f69cc497f4b68f3f49029f5b2b7a3209d8d890e 100644 (file)
@@ -12,27 +12,6 @@ struct task_struct *__switch_to_asm(struct task_struct *prev,
 __visible struct task_struct *__switch_to(struct task_struct *prev,
                                          struct task_struct *next);
 
-/* This runs runs on the previous thread's stack. */
-static inline void prepare_switch_to(struct task_struct *next)
-{
-#ifdef CONFIG_VMAP_STACK
-       /*
-        * If we switch to a stack that has a top-level paging entry
-        * that is not present in the current mm, the resulting #PF will
-        * will be promoted to a double-fault and we'll panic.  Probe
-        * the new stack now so that vmalloc_fault can fix up the page
-        * tables if needed.  This can only happen if we use a stack
-        * in vmap space.
-        *
-        * We assume that the stack is aligned so that it never spans
-        * more than one top-level paging entry.
-        *
-        * To minimize cache pollution, just follow the stack pointer.
-        */
-       READ_ONCE(*(unsigned char *)next->thread.sp);
-#endif
-}
-
 asmlinkage void ret_from_fork(void);
 
 /*
@@ -67,8 +46,6 @@ struct fork_frame {
 
 #define switch_to(prev, next, last)                                    \
 do {                                                                   \
-       prepare_switch_to(next);                                        \
-                                                                       \
        ((last) = __switch_to_asm((prev), (next)));                     \
 } while (0)
 
index c26a7e1d8a2c491ca6f004a716031393a5f35ece..2ae904bf25e423b8627e162c2118b5ea6e8c801e 100644 (file)
@@ -69,9 +69,7 @@ dotraplinkage void do_overflow(struct pt_regs *regs, long error_code);
 dotraplinkage void do_bounds(struct pt_regs *regs, long error_code);
 dotraplinkage void do_invalid_op(struct pt_regs *regs, long error_code);
 dotraplinkage void do_device_not_available(struct pt_regs *regs, long error_code);
-#if defined(CONFIG_X86_64) || defined(CONFIG_DOUBLEFAULT)
 dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsigned long cr2);
-#endif
 dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *regs, long error_code);
 dotraplinkage void do_invalid_TSS(struct pt_regs *regs, long error_code);
 dotraplinkage void do_segment_not_present(struct pt_regs *regs, long error_code);
@@ -118,11 +116,6 @@ void smp_spurious_interrupt(struct pt_regs *regs);
 void smp_error_interrupt(struct pt_regs *regs);
 asmlinkage void smp_irq_move_cleanup_interrupt(void);
 
-extern void ist_enter(struct pt_regs *regs);
-extern void ist_exit(struct pt_regs *regs);
-extern void ist_begin_non_atomic(struct pt_regs *regs);
-extern void ist_end_non_atomic(void);
-
 #ifdef CONFIG_VMAP_STACK
 void __noreturn handle_stack_overflow(const char *message,
                                      struct pt_regs *regs,
index f5e2eb12cb71e2d168c1bf525caf609e7dcf1d9a..7d903fdb3f4392e1ec8d04db40ba54daa3ddf1db 100644 (file)
        UNWIND_HINT sp_offset=\sp_offset
 .endm
 
-.macro UNWIND_HINT_SAVE
-       UNWIND_HINT type=UNWIND_HINT_TYPE_SAVE
-.endm
-
-.macro UNWIND_HINT_RESTORE
-       UNWIND_HINT type=UNWIND_HINT_TYPE_RESTORE
+/*
+ * RET_OFFSET: Used on instructions that terminate a function; mostly RETURN
+ * and sibling calls. On these, sp_offset denotes the expected offset from
+ * initial_func_cfi.
+ */
+.macro UNWIND_HINT_RET_OFFSET sp_offset=8
+       UNWIND_HINT type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
 .endm
 
-#else /* !__ASSEMBLY__ */
-
-#define UNWIND_HINT(sp_reg, sp_offset, type, end)              \
-       "987: \n\t"                                             \
-       ".pushsection .discard.unwind_hints\n\t"                \
-       /* struct unwind_hint */                                \
-       ".long 987b - .\n\t"                                    \
-       ".short " __stringify(sp_offset) "\n\t"                 \
-       ".byte " __stringify(sp_reg) "\n\t"                     \
-       ".byte " __stringify(type) "\n\t"                       \
-       ".byte " __stringify(end) "\n\t"                        \
-       ".balign 4 \n\t"                                        \
-       ".popsection\n\t"
-
-#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE, 0)
-
-#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE, 0)
-
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_X86_UNWIND_HINTS_H */
index 389174eaec7941341bac35d66767eefeb72e46f2..2fcc3ac12e76cdeac1b3e5e72783a18d70b0de60 100644 (file)
@@ -123,12 +123,6 @@ enum uv_memprotect {
        UV_MEMPROT_ALLOW_RW
 };
 
-/*
- * bios calls have 6 parameters
- */
-extern s64 uv_bios_call(enum uv_bios_cmd, u64, u64, u64, u64, u64);
-extern s64 uv_bios_call_irqsave(enum uv_bios_cmd, u64, u64, u64, u64, u64);
-
 extern s64 uv_bios_get_sn_info(int, int *, long *, long *, long *, long *);
 extern s64 uv_bios_freq_base(u64, u64 *);
 extern int uv_bios_mq_watchlist_alloc(unsigned long, unsigned int,
@@ -146,7 +140,6 @@ extern long sn_partition_id;
 extern long sn_coherency_id;
 extern long sn_region_size;
 extern long system_serial_number;
-#define uv_partition_coherence_id()    (sn_coherency_id)
 
 extern struct kobject *sgi_uv_kobj;    /* /sys/firmware/sgi_uv */
 
index 45ea95ce79b461b83929acc9a199fb9196ee1497..ae587ce544f44667ee0b439ac12604efe9b0d07e 100644 (file)
@@ -31,7 +31,6 @@ static inline bool is_early_uv_system(void)
 }
 extern int is_uv_system(void);
 extern int is_uv_hubbed(int uvtype);
-extern int is_uv_hubless(int uvtype);
 extern void uv_cpu_init(void);
 extern void uv_nmi_init(void);
 extern void uv_system_init(void);
@@ -44,7 +43,6 @@ static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; }
 static inline bool is_early_uv_system(void)    { return 0; }
 static inline int is_uv_system(void)   { return 0; }
 static inline int is_uv_hubbed(int uv) { return 0; }
-static inline int is_uv_hubless(int uv) { return 0; }
 static inline void uv_cpu_init(void)   { }
 static inline void uv_system_init(void)        { }
 static inline const struct cpumask *
index 950cd1395d5dd3bc65d5d03514d0d7236fe2f54d..60ca0afdeaf9f67c31a214952363e5c930020a02 100644 (file)
@@ -219,20 +219,6 @@ static inline struct uv_hub_info_s *uv_cpu_hub_info(int cpu)
        return (struct uv_hub_info_s *)uv_cpu_info_per(cpu)->p_uv_hub_info;
 }
 
-#define        UV_HUB_INFO_VERSION     0x7150
-extern int uv_hub_info_version(void);
-static inline int uv_hub_info_check(int version)
-{
-       if (uv_hub_info_version() == version)
-               return 0;
-
-       pr_crit("UV: uv_hub_info version(%x) mismatch, expecting(%x)\n",
-               uv_hub_info_version(), version);
-
-       BUG();  /* Catastrophic - cannot continue on unknown UV system */
-}
-#define        _uv_hub_info_check()    uv_hub_info_check(UV_HUB_INFO_VERSION)
-
 /*
  * HUB revision ranges for each UV HUB architecture.
  * This is a software convention - NOT the hardware revision numbers in
@@ -244,51 +230,32 @@ static inline int uv_hub_info_check(int version)
 #define UV4_HUB_REVISION_BASE          7
 #define UV4A_HUB_REVISION_BASE         8       /* UV4 (fixed) rev 2 */
 
-/* WARNING: UVx_HUB_IS_SUPPORTED defines are deprecated and will be removed */
 static inline int is_uv1_hub(void)
 {
-#ifdef UV1_HUB_IS_SUPPORTED
        return is_uv_hubbed(uv(1));
-#else
-       return 0;
-#endif
 }
 
 static inline int is_uv2_hub(void)
 {
-#ifdef UV2_HUB_IS_SUPPORTED
        return is_uv_hubbed(uv(2));
-#else
-       return 0;
-#endif
 }
 
 static inline int is_uv3_hub(void)
 {
-#ifdef UV3_HUB_IS_SUPPORTED
        return is_uv_hubbed(uv(3));
-#else
-       return 0;
-#endif
 }
 
 /* First test "is UV4A", then "is UV4" */
 static inline int is_uv4a_hub(void)
 {
-#ifdef UV4A_HUB_IS_SUPPORTED
        if (is_uv_hubbed(uv(4)))
                return (uv_hub_info->hub_revision == UV4A_HUB_REVISION_BASE);
-#endif
        return 0;
 }
 
 static inline int is_uv4_hub(void)
 {
-#ifdef UV4_HUB_IS_SUPPORTED
        return is_uv_hubbed(uv(4));
-#else
-       return 0;
-#endif
 }
 
 static inline int is_uvx_hub(void)
@@ -692,7 +659,6 @@ static inline int uv_cpu_blade_processor_id(int cpu)
 {
        return uv_cpu_info_per(cpu)->blade_cpu_id;
 }
-#define _uv_cpu_blade_processor_id 1   /* indicate function available */
 
 /* Blade number to Node number (UV1..UV4 is 1:1) */
 static inline int uv_blade_to_node(int blade)
@@ -856,26 +822,6 @@ static inline void uv_set_cpu_scir_bits(int cpu, unsigned char value)
 }
 
 extern unsigned int uv_apicid_hibits;
-static unsigned long uv_hub_ipi_value(int apicid, int vector, int mode)
-{
-       apicid |= uv_apicid_hibits;
-       return (1UL << UVH_IPI_INT_SEND_SHFT) |
-                       ((apicid) << UVH_IPI_INT_APIC_ID_SHFT) |
-                       (mode << UVH_IPI_INT_DELIVERY_MODE_SHFT) |
-                       (vector << UVH_IPI_INT_VECTOR_SHFT);
-}
-
-static inline void uv_hub_send_ipi(int pnode, int apicid, int vector)
-{
-       unsigned long val;
-       unsigned long dmode = dest_Fixed;
-
-       if (vector == NMI_VECTOR)
-               dmode = dest_NMI;
-
-       val = uv_hub_ipi_value(apicid, vector, dmode);
-       uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
-}
 
 /*
  * Get the minimum revision number of the hub chips within the partition.
index 62c79e26a59ace46ac4b7fc4a3a400e79dbedb70..9ee5ed6e8b3489ba251121ad50b7e83cbf4d1719 100644 (file)
 #define UV3_HUB_PART_NUMBER_X  0x4321
 #define UV4_HUB_PART_NUMBER    0x99a1
 
-/* Compat: Indicate which UV Hubs are supported. */
-#define UV1_HUB_IS_SUPPORTED   1
-#define UV2_HUB_IS_SUPPORTED   1
-#define UV3_HUB_IS_SUPPORTED   1
-#define UV4_HUB_IS_SUPPORTED   1
-#define UV4A_HUB_IS_SUPPORTED  1
-
 /* Error function to catch undefined references */
 extern unsigned long uv_undefined(char *str);
 
index 196fdd02b8b1b3d71ac994715f7dee8fa8b5fa52..be5e2e747f507657efc74f5ed2b68ed262103fda 100644 (file)
@@ -2,8 +2,15 @@
 #ifndef _UAPI_ASM_X86_UNISTD_H
 #define _UAPI_ASM_X86_UNISTD_H
 
-/* x32 syscall flag bit */
-#define __X32_SYSCALL_BIT      0x40000000UL
+/*
+ * x32 syscall flag bit.  Some user programs expect syscall NR macros
+ * and __X32_SYSCALL_BIT to have type int, even though syscall numbers
+ * are, for practical purposes, unsigned long.
+ *
+ * Fortunately, expressions like (nr & ~__X32_SYSCALL_BIT) do the right
+ * thing regardless.
+ */
+#define __X32_SYSCALL_BIT      0x40000000
 
 #ifndef __KERNEL__
 # ifdef __i386__
index ba89cabe5fcf1136cb42d9cb6fa24c75e401bd0c..2a7c3afa62e2ef308fdb1294395c31efb11cc3a1 100644 (file)
@@ -102,9 +102,7 @@ obj-$(CONFIG_KEXEC_FILE)    += kexec-bzimage64.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump_$(BITS).o
 obj-y                          += kprobes/
 obj-$(CONFIG_MODULES)          += module.o
-ifeq ($(CONFIG_X86_32),y)
-obj-$(CONFIG_DOUBLEFAULT)      += doublefault_32.o
-endif
+obj-$(CONFIG_X86_32)           += doublefault_32.o
 obj-$(CONFIG_KGDB)             += kgdb.o
 obj-$(CONFIG_VM86)             += vm86_32.o
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
index fe698f96617ca7ee9bbdde5107af1abc58fc17e2..263eeaddb0aa035f22c9981a1f88ba9235aa03cd 100644 (file)
@@ -345,56 +345,3 @@ out_noapbt:
        apb_timer_block_enabled = 0;
        panic("failed to enable APB timer\n");
 }
-
-/* called before apb_timer_enable, use early map */
-unsigned long apbt_quick_calibrate(void)
-{
-       int i, scale;
-       u64 old, new;
-       u64 t1, t2;
-       unsigned long khz = 0;
-       u32 loop, shift;
-
-       apbt_set_mapping();
-       dw_apb_clocksource_start(clocksource_apbt);
-
-       /* check if the timer can count down, otherwise return */
-       old = dw_apb_clocksource_read(clocksource_apbt);
-       i = 10000;
-       while (--i) {
-               if (old != dw_apb_clocksource_read(clocksource_apbt))
-                       break;
-       }
-       if (!i)
-               goto failed;
-
-       /* count 16 ms */
-       loop = (apbt_freq / 1000) << 4;
-
-       /* restart the timer to ensure it won't get to 0 in the calibration */
-       dw_apb_clocksource_start(clocksource_apbt);
-
-       old = dw_apb_clocksource_read(clocksource_apbt);
-       old += loop;
-
-       t1 = rdtsc();
-
-       do {
-               new = dw_apb_clocksource_read(clocksource_apbt);
-       } while (new < old);
-
-       t2 = rdtsc();
-
-       shift = 5;
-       if (unlikely(loop >> shift == 0)) {
-               printk(KERN_INFO
-                      "APBT TSC calibration failed, not enough resolution\n");
-               return 0;
-       }
-       scale = (int)div_u64((t2 - t1), loop >> shift);
-       khz = (scale * (apbt_freq / 1000)) >> shift;
-       printk(KERN_INFO "TSC freq calculated by APB timer is %lu khz\n", khz);
-       return khz;
-failed:
-       return 0;
-}
index e53dda210cd734235eace14d0deafb65bc5e8ec0..4b1d31be50b4a1aba2816c2a51bc89affbe96f14 100644 (file)
@@ -544,46 +544,20 @@ static struct clock_event_device lapic_clockevent = {
 };
 static DEFINE_PER_CPU(struct clock_event_device, lapic_events);
 
-static __init u32 hsx_deadline_rev(void)
-{
-       switch (boot_cpu_data.x86_stepping) {
-       case 0x02: return 0x3a; /* EP */
-       case 0x04: return 0x0f; /* EX */
-       }
-
-       return ~0U;
-}
-
-static __init u32 bdx_deadline_rev(void)
-{
-       switch (boot_cpu_data.x86_stepping) {
-       case 0x02: return 0x00000011;
-       case 0x03: return 0x0700000e;
-       case 0x04: return 0x0f00000c;
-       case 0x05: return 0x0e000003;
-       }
-
-       return ~0U;
-}
-
-static __init u32 skx_deadline_rev(void)
-{
-       switch (boot_cpu_data.x86_stepping) {
-       case 0x03: return 0x01000136;
-       case 0x04: return 0x02000014;
-       }
+static const struct x86_cpu_id deadline_match[] __initconst = {
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(HASWELL_X, X86_STEPPINGS(0x2, 0x2), 0x3a), /* EP */
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(HASWELL_X, X86_STEPPINGS(0x4, 0x4), 0x0f), /* EX */
 
-       if (boot_cpu_data.x86_stepping > 4)
-               return 0;
+       X86_MATCH_INTEL_FAM6_MODEL( BROADWELL_X,        0x0b000020),
 
-       return ~0U;
-}
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(BROADWELL_D, X86_STEPPINGS(0x2, 0x2), 0x00000011),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(BROADWELL_D, X86_STEPPINGS(0x3, 0x3), 0x0700000e),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(BROADWELL_D, X86_STEPPINGS(0x4, 0x4), 0x0f00000c),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(BROADWELL_D, X86_STEPPINGS(0x5, 0x5), 0x0e000003),
 
-static const struct x86_cpu_id deadline_match[] __initconst = {
-       X86_MATCH_INTEL_FAM6_MODEL( HASWELL_X,          &hsx_deadline_rev),
-       X86_MATCH_INTEL_FAM6_MODEL( BROADWELL_X,        0x0b000020),
-       X86_MATCH_INTEL_FAM6_MODEL( BROADWELL_D,        &bdx_deadline_rev),
-       X86_MATCH_INTEL_FAM6_MODEL( SKYLAKE_X,          &skx_deadline_rev),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SKYLAKE_X, X86_STEPPINGS(0x3, 0x3), 0x01000136),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SKYLAKE_X, X86_STEPPINGS(0x4, 0x4), 0x02000014),
+       X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SKYLAKE_X, X86_STEPPINGS(0x5, 0xf), 0),
 
        X86_MATCH_INTEL_FAM6_MODEL( HASWELL,            0x22),
        X86_MATCH_INTEL_FAM6_MODEL( HASWELL_L,          0x20),
@@ -615,14 +589,7 @@ static __init bool apic_validate_deadline_timer(void)
        if (!m)
                return true;
 
-       /*
-        * Function pointers will have the MSB set due to address layout,
-        * immediate revisions will not.
-        */
-       if ((long)m->driver_data < 0)
-               rev = ((u32 (*)(void))(m->driver_data))();
-       else
-               rev = (u32)m->driver_data;
+       rev = (u32)m->driver_data;
 
        if (boot_cpu_data.microcode >= rev)
                return true;
index 913c88617848b54e7aec184fd04246a5ed27fba0..ce61e3e7d39944eef4866a0f50bac28b2a25cf42 100644 (file)
@@ -154,19 +154,6 @@ static inline bool mp_is_legacy_irq(int irq)
        return irq >= 0 && irq < nr_legacy_irqs();
 }
 
-/*
- * Initialize all legacy IRQs and all pins on the first IOAPIC
- * if we have legacy interrupt controller. Kernel boot option "pirq="
- * may rely on non-legacy pins on the first IOAPIC.
- */
-static inline int mp_init_irq_at_boot(int ioapic, int irq)
-{
-       if (!nr_legacy_irqs())
-               return 0;
-
-       return ioapic == 0 || mp_is_legacy_irq(irq);
-}
-
 static inline struct irq_domain *mp_ioapic_irqdomain(int ioapic)
 {
        return ioapics[ioapic].irqdomain;
index ad53b2abc859fe017676c4cf97d8add35455f42b..69e70ed0f5e6de12521e962ce3710f946ac3e735 100644 (file)
@@ -30,8 +30,6 @@ static enum uv_system_type    uv_system_type;
 static int                     uv_hubbed_system;
 static int                     uv_hubless_system;
 static u64                     gru_start_paddr, gru_end_paddr;
-static u64                     gru_dist_base, gru_first_node_paddr = -1LL, gru_last_node_paddr;
-static u64                     gru_dist_lmask, gru_dist_umask;
 static union uvh_apicid                uvh_apicid;
 
 /* Unpack OEM/TABLE ID's to be NULL terminated strings */
@@ -48,11 +46,9 @@ static struct {
        unsigned int gnode_shift;
 } uv_cpuid;
 
-int uv_min_hub_revision_id;
-EXPORT_SYMBOL_GPL(uv_min_hub_revision_id);
+static int uv_min_hub_revision_id;
 
 unsigned int uv_apicid_hibits;
-EXPORT_SYMBOL_GPL(uv_apicid_hibits);
 
 static struct apic apic_x2apic_uv_x;
 static struct uv_hub_info_s uv_hub_info_node0;
@@ -85,20 +81,7 @@ static unsigned long __init uv_early_read_mmr(unsigned long addr)
 
 static inline bool is_GRU_range(u64 start, u64 end)
 {
-       if (gru_dist_base) {
-               u64 su = start & gru_dist_umask; /* Upper (incl pnode) bits */
-               u64 sl = start & gru_dist_lmask; /* Base offset bits */
-               u64 eu = end & gru_dist_umask;
-               u64 el = end & gru_dist_lmask;
-
-               /* Must reside completely within a single GRU range: */
-               return (sl == gru_dist_base && el == gru_dist_base &&
-                       su >= gru_first_node_paddr &&
-                       su <= gru_last_node_paddr &&
-                       eu == su);
-       } else {
-               return start >= gru_start_paddr && end <= gru_end_paddr;
-       }
+       return start >= gru_start_paddr && end <= gru_end_paddr;
 }
 
 static bool uv_is_untracked_pat_range(u64 start, u64 end)
@@ -385,11 +368,10 @@ int is_uv_hubbed(int uvtype)
 }
 EXPORT_SYMBOL_GPL(is_uv_hubbed);
 
-int is_uv_hubless(int uvtype)
+static int is_uv_hubless(int uvtype)
 {
        return (uv_hubless_system & uvtype);
 }
-EXPORT_SYMBOL_GPL(is_uv_hubless);
 
 void **__uv_hub_info_list;
 EXPORT_SYMBOL_GPL(__uv_hub_info_list);
@@ -417,12 +399,6 @@ static __initdata struct uv_gam_range_s            *_gr_table;
 
 #define        SOCK_EMPTY      ((unsigned short)~0)
 
-extern int uv_hub_info_version(void)
-{
-       return UV_HUB_INFO_VERSION;
-}
-EXPORT_SYMBOL(uv_hub_info_version);
-
 /* Default UV memory block size is 2GB */
 static unsigned long mem_block_size __initdata = (2UL << 30);
 
@@ -590,12 +566,21 @@ static int uv_wakeup_secondary(int phys_apicid, unsigned long start_rip)
 
 static void uv_send_IPI_one(int cpu, int vector)
 {
-       unsigned long apicid;
-       int pnode;
+       unsigned long apicid = per_cpu(x86_cpu_to_apicid, cpu);
+       int pnode = uv_apicid_to_pnode(apicid);
+       unsigned long dmode, val;
+
+       if (vector == NMI_VECTOR)
+               dmode = dest_NMI;
+       else
+               dmode = dest_Fixed;
+
+       val = (1UL << UVH_IPI_INT_SEND_SHFT) |
+               ((apicid | uv_apicid_hibits) << UVH_IPI_INT_APIC_ID_SHFT) |
+               (dmode << UVH_IPI_INT_DELIVERY_MODE_SHFT) |
+               (vector << UVH_IPI_INT_VECTOR_SHFT);
 
-       apicid = per_cpu(x86_cpu_to_apicid, cpu);
-       pnode = uv_apicid_to_pnode(apicid);
-       uv_hub_send_ipi(pnode, apicid, vector);
+       uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
 }
 
 static void uv_send_IPI_mask(const struct cpumask *mask, int vector)
@@ -797,42 +782,6 @@ static __init void map_high(char *id, unsigned long base, int pshift, int bshift
                init_extra_mapping_wb(paddr, bytes);
 }
 
-static __init void map_gru_distributed(unsigned long c)
-{
-       union uvh_rh_gam_gru_overlay_config_mmr_u gru;
-       u64 paddr;
-       unsigned long bytes;
-       int nid;
-
-       gru.v = c;
-
-       /* Only base bits 42:28 relevant in dist mode */
-       gru_dist_base = gru.v & 0x000007fff0000000UL;
-       if (!gru_dist_base) {
-               pr_info("UV: Map GRU_DIST base address NULL\n");
-               return;
-       }
-
-       bytes = 1UL << UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR_BASE_SHFT;
-       gru_dist_lmask = ((1UL << uv_hub_info->m_val) - 1) & ~(bytes - 1);
-       gru_dist_umask = ~((1UL << uv_hub_info->m_val) - 1);
-       gru_dist_base &= gru_dist_lmask; /* Clear bits above M */
-
-       for_each_online_node(nid) {
-               paddr = ((u64)uv_node_to_pnode(nid) << uv_hub_info->m_val) |
-                               gru_dist_base;
-               init_extra_mapping_wb(paddr, bytes);
-               gru_first_node_paddr = min(paddr, gru_first_node_paddr);
-               gru_last_node_paddr = max(paddr, gru_last_node_paddr);
-       }
-
-       /* Save upper (63:M) bits of address only for is_GRU_range */
-       gru_first_node_paddr &= gru_dist_umask;
-       gru_last_node_paddr &= gru_dist_umask;
-
-       pr_debug("UV: Map GRU_DIST base 0x%016llx  0x%016llx - 0x%016llx\n", gru_dist_base, gru_first_node_paddr, gru_last_node_paddr);
-}
-
 static __init void map_gru_high(int max_pnode)
 {
        union uvh_rh_gam_gru_overlay_config_mmr_u gru;
@@ -846,12 +795,6 @@ static __init void map_gru_high(int max_pnode)
                return;
        }
 
-       /* Only UV3 has distributed GRU mode */
-       if (is_uv3_hub() && gru.s3.mode) {
-               map_gru_distributed(gru.v);
-               return;
-       }
-
        base = (gru.v & mask) >> shift;
        map_high("GRU", base, shift, shift, max_pnode, map_wb);
        gru_start_paddr = ((u64)base << shift);
index e1efe44ebefc7ba2a18ff1ca1fa913d09259a7c5..83d9cad4e68bd33e06dd61d0db49319cd11ece79 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/types.h>
 #include <linux/audit.h>
 #include <asm/unistd.h>
+#include <asm/audit.h>
 
 static unsigned dir_class[] = {
 #include <asm-generic/audit_dir_write.h>
@@ -41,7 +42,6 @@ int audit_classify_arch(int arch)
 int audit_classify_syscall(int abi, unsigned syscall)
 {
 #ifdef CONFIG_IA32_EMULATION
-       extern int ia32_classify_syscall(unsigned);
        if (abi == AUDIT_ARCH_I386)
                return ia32_classify_syscall(syscall);
 #endif
index 547ad7bbf0e016663a82a608634b906f01a04b10..d4806eac9325ca13e0cf98cfe4c70f3a141cb63e 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/pci-direct.h>
 #include <asm/delay.h>
 #include <asm/debugreg.h>
+#include <asm/resctrl.h>
 
 #ifdef CONFIG_X86_64
 # include <asm/mmconfig.h>
@@ -597,6 +598,8 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
                        x86_amd_ls_cfg_ssbd_mask = 1ULL << bit;
                }
        }
+
+       resctrl_cpu_detect(c);
 }
 
 static void early_detect_mem_encrypt(struct cpuinfo_x86 *c)
@@ -1142,8 +1145,7 @@ static const int amd_erratum_383[] =
 
 /* #1054: Instructions Retired Performance Counter May Be Inaccurate */
 static const int amd_erratum_1054[] =
-       AMD_OSVW_ERRATUM(0, AMD_MODEL_RANGE(0x17, 0, 0, 0x2f, 0xf));
-
+       AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x17, 0, 0, 0x2f, 0xf));
 
 static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
 {
index bed0cb83fe245a052bab3b7c2f3c1718c54ff444..d07809286b95eee854691539006a07150e9f462d 100644 (file)
@@ -854,30 +854,6 @@ static void init_speculation_control(struct cpuinfo_x86 *c)
        }
 }
 
-static void init_cqm(struct cpuinfo_x86 *c)
-{
-       if (!cpu_has(c, X86_FEATURE_CQM_LLC)) {
-               c->x86_cache_max_rmid  = -1;
-               c->x86_cache_occ_scale = -1;
-               return;
-       }
-
-       /* will be overridden if occupancy monitoring exists */
-       c->x86_cache_max_rmid = cpuid_ebx(0xf);
-
-       if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC) ||
-           cpu_has(c, X86_FEATURE_CQM_MBM_TOTAL) ||
-           cpu_has(c, X86_FEATURE_CQM_MBM_LOCAL)) {
-               u32 eax, ebx, ecx, edx;
-
-               /* QoS sub-leaf, EAX=0Fh, ECX=1 */
-               cpuid_count(0xf, 1, &eax, &ebx, &ecx, &edx);
-
-               c->x86_cache_max_rmid  = ecx;
-               c->x86_cache_occ_scale = ebx;
-       }
-}
-
 void get_cpu_cap(struct cpuinfo_x86 *c)
 {
        u32 eax, ebx, ecx, edx;
@@ -945,7 +921,6 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
 
        init_scattered_cpuid_features(c);
        init_speculation_control(c);
-       init_cqm(c);
 
        /*
         * Clear/Set all flags overridden by options, after probe.
@@ -1377,20 +1352,6 @@ static void generic_identify(struct cpuinfo_x86 *c)
 #endif
 }
 
-static void x86_init_cache_qos(struct cpuinfo_x86 *c)
-{
-       /*
-        * The heavy lifting of max_rmid and cache_occ_scale are handled
-        * in get_cpu_cap().  Here we just set the max_rmid for the boot_cpu
-        * in case CQM bits really aren't there in this CPU.
-        */
-       if (c != &boot_cpu_data) {
-               boot_cpu_data.x86_cache_max_rmid =
-                       min(boot_cpu_data.x86_cache_max_rmid,
-                           c->x86_cache_max_rmid);
-       }
-}
-
 /*
  * Validate that ACPI/mptables have the same information about the
  * effective APIC id and update the package map.
@@ -1503,7 +1464,6 @@ static void identify_cpu(struct cpuinfo_x86 *c)
 #endif
 
        x86_init_rdrand(c);
-       x86_init_cache_qos(c);
        setup_pku(c);
 
        /*
index a19a680542ce736ce0f89c3621d0695a9481c6f8..166d7c355896bf763e53f26f2d4bbc0e4feb40db 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/cpu_device_id.h>
 #include <asm/cmdline.h>
 #include <asm/traps.h>
+#include <asm/resctrl.h>
 
 #ifdef CONFIG_X86_64
 #include <linux/topology.h>
@@ -322,6 +323,11 @@ static void early_init_intel(struct cpuinfo_x86 *c)
                detect_ht_early(c);
 }
 
+static void bsp_init_intel(struct cpuinfo_x86 *c)
+{
+       resctrl_cpu_detect(c);
+}
+
 #ifdef CONFIG_X86_32
 /*
  *     Early probe support logic for ppro memory erratum #50
@@ -961,6 +967,7 @@ static const struct cpu_dev intel_cpu_dev = {
 #endif
        .c_detect_tlb   = intel_detect_tlb,
        .c_early_init   = early_init_intel,
+       .c_bsp_init     = bsp_init_intel,
        .c_init         = init_intel,
        .c_x86_vendor   = X86_VENDOR_INTEL,
 };
index d3482eb43ff3e6c954f34958a7caafb1ef3dc429..ad6776081e60daedd4ea8c5efc41e033440ef14a 100644 (file)
@@ -39,13 +39,18 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
        const struct x86_cpu_id *m;
        struct cpuinfo_x86 *c = &boot_cpu_data;
 
-       for (m = match; m->vendor | m->family | m->model | m->feature; m++) {
+       for (m = match;
+            m->vendor | m->family | m->model | m->steppings | m->feature;
+            m++) {
                if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
                        continue;
                if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
                        continue;
                if (m->model != X86_MODEL_ANY && c->x86_model != m->model)
                        continue;
+               if (m->steppings != X86_STEPPING_ANY &&
+                   !(BIT(c->x86_stepping) & m->steppings))
+                       continue;
                if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature))
                        continue;
                return m;
index 54165f3569e8821b3308650b86d69dd092011181..e9265e2f28c9636153cb1ac8372dd09d6224821f 100644 (file)
@@ -42,6 +42,8 @@
 #include <linux/export.h>
 #include <linux/jump_label.h>
 #include <linux/set_memory.h>
+#include <linux/task_work.h>
+#include <linux/hardirq.h>
 
 #include <asm/intel-family.h>
 #include <asm/processor.h>
@@ -1086,23 +1088,6 @@ static void mce_clear_state(unsigned long *toclear)
        }
 }
 
-static int do_memory_failure(struct mce *m)
-{
-       int flags = MF_ACTION_REQUIRED;
-       int ret;
-
-       pr_err("Uncorrected hardware memory error in user-access at %llx", m->addr);
-       if (!(m->mcgstatus & MCG_STATUS_RIPV))
-               flags |= MF_MUST_KILL;
-       ret = memory_failure(m->addr >> PAGE_SHIFT, flags);
-       if (ret)
-               pr_err("Memory error not recovered");
-       else
-               set_mce_nospec(m->addr >> PAGE_SHIFT);
-       return ret;
-}
-
-
 /*
  * Cases where we avoid rendezvous handler timeout:
  * 1) If this CPU is offline.
@@ -1204,6 +1189,29 @@ static void __mc_scan_banks(struct mce *m, struct mce *final,
        *m = *final;
 }
 
+static void kill_me_now(struct callback_head *ch)
+{
+       force_sig(SIGBUS);
+}
+
+static void kill_me_maybe(struct callback_head *cb)
+{
+       struct task_struct *p = container_of(cb, struct task_struct, mce_kill_me);
+       int flags = MF_ACTION_REQUIRED;
+
+       pr_err("Uncorrected hardware memory error in user-access at %llx", p->mce_addr);
+       if (!(p->mce_status & MCG_STATUS_RIPV))
+               flags |= MF_MUST_KILL;
+
+       if (!memory_failure(p->mce_addr >> PAGE_SHIFT, flags)) {
+               set_mce_nospec(p->mce_addr >> PAGE_SHIFT);
+               return;
+       }
+
+       pr_err("Memory error not recovered");
+       kill_me_now(cb);
+}
+
 /*
  * The actual machine check handler. This only handles real
  * exceptions when something got corrupted coming in through int 18.
@@ -1222,7 +1230,7 @@ static void __mc_scan_banks(struct mce *m, struct mce *final,
  * backing the user stack, tracing that reads the user stack will cause
  * potentially infinite recursion.
  */
-void notrace do_machine_check(struct pt_regs *regs, long error_code)
+void noinstr do_machine_check(struct pt_regs *regs, long error_code)
 {
        DECLARE_BITMAP(valid_banks, MAX_NR_BANKS);
        DECLARE_BITMAP(toclear, MAX_NR_BANKS);
@@ -1259,7 +1267,7 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code)
        if (__mc_check_crashing_cpu(cpu))
                return;
 
-       ist_enter(regs);
+       nmi_enter();
 
        this_cpu_inc(mce_exception_count);
 
@@ -1352,23 +1360,24 @@ void notrace do_machine_check(struct pt_regs *regs, long error_code)
 
        /* Fault was in user mode and we need to take some action */
        if ((m.cs & 3) == 3) {
-               ist_begin_non_atomic(regs);
-               local_irq_enable();
-
-               if (kill_it || do_memory_failure(&m))
-                       force_sig(SIGBUS);
-               local_irq_disable();
-               ist_end_non_atomic();
+               /* If this triggers there is no way to recover. Die hard. */
+               BUG_ON(!on_thread_stack() || !user_mode(regs));
+
+               current->mce_addr = m.addr;
+               current->mce_status = m.mcgstatus;
+               current->mce_kill_me.func = kill_me_maybe;
+               if (kill_it)
+                       current->mce_kill_me.func = kill_me_now;
+               task_work_add(current, &current->mce_kill_me, true);
        } else {
                if (!fixup_exception(regs, X86_TRAP_MC, error_code, 0))
                        mce_panic("Failed kernel mode recovery", &m, msg);
        }
 
 out_ist:
-       ist_exit(regs);
+       nmi_exit();
 }
 EXPORT_SYMBOL_GPL(do_machine_check);
-NOKPROBE_SYMBOL(do_machine_check);
 
 #ifndef CONFIG_MEMORY_FAILURE
 int memory_failure(unsigned long pfn, int flags)
index 4ae6df556526e56a70db8dd41c004541d7fe6008..5ee94aa1b7668b45d9b62696a8d45291399813d9 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/smp.h>
+#include <linux/hardirq.h>
 
 #include <asm/processor.h>
 #include <asm/traps.h>
@@ -24,7 +25,7 @@ static void pentium_machine_check(struct pt_regs *regs, long error_code)
 {
        u32 loaddr, hi, lotype;
 
-       ist_enter(regs);
+       nmi_enter();
 
        rdmsr(MSR_IA32_P5_MC_ADDR, loaddr, hi);
        rdmsr(MSR_IA32_P5_MC_TYPE, lotype, hi);
@@ -39,7 +40,7 @@ static void pentium_machine_check(struct pt_regs *regs, long error_code)
 
        add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
 
-       ist_exit(regs);
+       nmi_exit();
 }
 
 /* Set up machine check reporting for processors with Intel style MCE: */
index a30ea13cccc2611148f6df52149abb712fd9107c..b3938c195365c17ced3c28869e721f6f13be50a6 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/hardirq.h>
 
 #include <asm/processor.h>
 #include <asm/traps.h>
 /* Machine check handler for WinChip C6: */
 static void winchip_machine_check(struct pt_regs *regs, long error_code)
 {
-       ist_enter(regs);
+       nmi_enter();
 
        pr_emerg("CPU0: Machine Check Exception.\n");
        add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE);
 
-       ist_exit(regs);
+       nmi_exit();
 }
 
 /* Set up machine check reporting on the Winchip C6 series */
index 7019d4b2df0c08fe24b530964aa1d81fffc190fd..baec68b7e010a8da4753789b5c4d5ff458c96934 100644 (file)
@@ -545,8 +545,7 @@ static int __wait_for_cpus(atomic_t *t, long long timeout)
 /*
  * Returns:
  * < 0 - on error
- *   0 - no update done
- *   1 - microcode was updated
+ *   0 - success (no update done or microcode was updated)
  */
 static int __reload_late(void *info)
 {
@@ -573,11 +572,11 @@ static int __reload_late(void *info)
        else
                goto wait_for_siblings;
 
-       if (err > UCODE_NFOUND) {
-               pr_warn("Error reloading microcode on CPU %d\n", cpu);
+       if (err >= UCODE_NFOUND) {
+               if (err == UCODE_ERROR)
+                       pr_warn("Error reloading microcode on CPU %d\n", cpu);
+
                ret = -1;
-       } else if (err == UCODE_UPDATED || err == UCODE_OK) {
-               ret = 1;
        }
 
 wait_for_siblings:
@@ -608,7 +607,7 @@ static int microcode_reload_late(void)
        atomic_set(&late_cpus_out, 0);
 
        ret = stop_machine_cpuslocked(__reload_late, NULL, cpu_online_mask);
-       if (ret > 0)
+       if (ret == 0)
                microcode_check();
 
        pr_info("Reload completed, microcode revision: 0x%x\n", boot_cpu_data.microcode);
@@ -649,7 +648,7 @@ static ssize_t reload_store(struct device *dev,
 put:
        put_online_cpus();
 
-       if (ret >= 0)
+       if (ret == 0)
                ret = size;
 
        return ret;
index 9556930cd8c1a4248db2937d63e82cfaec18a1f2..a5ee607a3b893252b099fab9b9403911106202fd 100644 (file)
@@ -63,6 +63,10 @@ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr)
                case 15:
                        return msr - MSR_P4_BPU_PERFCTR0;
                }
+               fallthrough;
+       case X86_VENDOR_ZHAOXIN:
+       case X86_VENDOR_CENTAUR:
+               return msr - MSR_ARCH_PERFMON_PERFCTR0;
        }
        return 0;
 }
@@ -92,6 +96,10 @@ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr)
                case 15:
                        return msr - MSR_P4_BSU_ESCR0;
                }
+               fallthrough;
+       case X86_VENDOR_ZHAOXIN:
+       case X86_VENDOR_CENTAUR:
+               return msr - MSR_ARCH_PERFMON_EVENTSEL0;
        }
        return 0;
 
index d8cc5223b7ce89e8dc3703bc714b0fe5d29d1d8e..12f967c6b603426995ff5ab45f29ef7593074574 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/cpuhotplug.h>
 
 #include <asm/intel-family.h>
-#include <asm/resctrl_sched.h>
+#include <asm/resctrl.h>
 #include "internal.h"
 
 /* Mutex to protect rdtgroup access. */
@@ -958,6 +958,36 @@ static __init void rdt_init_res_defs(void)
 
 static enum cpuhp_state rdt_online;
 
+/* Runs once on the BSP during boot. */
+void resctrl_cpu_detect(struct cpuinfo_x86 *c)
+{
+       if (!cpu_has(c, X86_FEATURE_CQM_LLC)) {
+               c->x86_cache_max_rmid  = -1;
+               c->x86_cache_occ_scale = -1;
+               c->x86_cache_mbm_width_offset = -1;
+               return;
+       }
+
+       /* will be overridden if occupancy monitoring exists */
+       c->x86_cache_max_rmid = cpuid_ebx(0xf);
+
+       if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC) ||
+           cpu_has(c, X86_FEATURE_CQM_MBM_TOTAL) ||
+           cpu_has(c, X86_FEATURE_CQM_MBM_LOCAL)) {
+               u32 eax, ebx, ecx, edx;
+
+               /* QoS sub-leaf, EAX=0Fh, ECX=1 */
+               cpuid_count(0xf, 1, &eax, &ebx, &ecx, &edx);
+
+               c->x86_cache_max_rmid  = ecx;
+               c->x86_cache_occ_scale = ebx;
+               if (c->x86_vendor == X86_VENDOR_INTEL)
+                       c->x86_cache_mbm_width_offset = eax & 0xff;
+               else
+                       c->x86_cache_mbm_width_offset = -1;
+       }
+}
+
 static int __init resctrl_late_init(void)
 {
        struct rdt_resource *r;
index 055c8613b5317afd6c550cab54ca94507b4e196a..934c8fb8a64a7d7131e3ec089828bdcd65e1e8fc 100644 (file)
@@ -495,14 +495,16 @@ int rdtgroup_schemata_show(struct kernfs_open_file *of,
        return ret;
 }
 
-void mon_event_read(struct rmid_read *rr, struct rdt_domain *d,
-                   struct rdtgroup *rdtgrp, int evtid, int first)
+void mon_event_read(struct rmid_read *rr, struct rdt_resource *r,
+                   struct rdt_domain *d, struct rdtgroup *rdtgrp,
+                   int evtid, int first)
 {
        /*
         * setup the parameters to send to the IPI to read the data.
         */
        rr->rgrp = rdtgrp;
        rr->evtid = evtid;
+       rr->r = r;
        rr->d = d;
        rr->val = 0;
        rr->first = first;
@@ -539,7 +541,7 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg)
                goto out;
        }
 
-       mon_event_read(&rr, d, rdtgrp, evtid, false);
+       mon_event_read(&rr, r, d, rdtgrp, evtid, false);
 
        if (rr.val & RMID_VAL_ERROR)
                seq_puts(m, "Error\n");
index 3dd13f3a8b23187c2b3e0257873c434960b33bae..f20a47d120b1bc5c5d869dbb6485f18fe8d1b75c 100644 (file)
@@ -31,7 +31,7 @@
 
 #define CQM_LIMBOCHECK_INTERVAL        1000
 
-#define MBM_CNTR_WIDTH                 24
+#define MBM_CNTR_WIDTH_BASE            24
 #define MBM_OVERFLOW_INTERVAL          1000
 #define MAX_MBA_BW                     100u
 #define MBA_IS_LINEAR                  0x4
 
 #define RMID_VAL_ERROR                 BIT_ULL(63)
 #define RMID_VAL_UNAVAIL               BIT_ULL(62)
+/*
+ * With the above fields in use 62 bits remain in MSR_IA32_QM_CTR for
+ * data to be returned. The counter width is discovered from the hardware
+ * as an offset from MBM_CNTR_WIDTH_BASE.
+ */
+#define MBM_CNTR_WIDTH_OFFSET_MAX (62 - MBM_CNTR_WIDTH_BASE)
 
 
 struct rdt_fs_context {
@@ -87,6 +93,7 @@ union mon_data_bits {
 
 struct rmid_read {
        struct rdtgroup         *rgrp;
+       struct rdt_resource     *r;
        struct rdt_domain       *d;
        int                     evtid;
        bool                    first;
@@ -460,6 +467,7 @@ struct rdt_resource {
        struct list_head        evt_list;
        int                     num_rmid;
        unsigned int            mon_scale;
+       unsigned int            mbm_width;
        unsigned long           fflags;
 };
 
@@ -587,8 +595,9 @@ void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r,
                                    unsigned int dom_id);
 void mkdir_mondata_subdir_allrdtgrp(struct rdt_resource *r,
                                    struct rdt_domain *d);
-void mon_event_read(struct rmid_read *rr, struct rdt_domain *d,
-                   struct rdtgroup *rdtgrp, int evtid, int first);
+void mon_event_read(struct rmid_read *rr, struct rdt_resource *r,
+                   struct rdt_domain *d, struct rdtgroup *rdtgrp,
+                   int evtid, int first);
 void mbm_setup_overflow_handler(struct rdt_domain *dom,
                                unsigned long delay_ms);
 void mbm_handle_overflow(struct work_struct *work);
index 773124b0e18ac0b1b6c48aedb6eed41dccde88cb..837d7d012b7b14c77759ccf487d99997873bc2de 100644 (file)
@@ -214,9 +214,9 @@ void free_rmid(u32 rmid)
                list_add_tail(&entry->list, &rmid_free_lru);
 }
 
-static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr)
+static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr, unsigned int width)
 {
-       u64 shift = 64 - MBM_CNTR_WIDTH, chunks;
+       u64 shift = 64 - width, chunks;
 
        chunks = (cur_msr << shift) - (prev_msr << shift);
        return chunks >>= shift;
@@ -256,7 +256,7 @@ static int __mon_event_count(u32 rmid, struct rmid_read *rr)
                return 0;
        }
 
-       chunks = mbm_overflow_count(m->prev_msr, tval);
+       chunks = mbm_overflow_count(m->prev_msr, tval, rr->r->mbm_width);
        m->chunks += chunks;
        m->prev_msr = tval;
 
@@ -278,7 +278,7 @@ static void mbm_bw_count(u32 rmid, struct rmid_read *rr)
        if (tval & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL))
                return;
 
-       chunks = mbm_overflow_count(m->prev_bw_msr, tval);
+       chunks = mbm_overflow_count(m->prev_bw_msr, tval, rr->r->mbm_width);
        m->chunks_bw += chunks;
        m->chunks = m->chunks_bw;
        cur_bw = (chunks * r->mon_scale) >> 20;
@@ -433,11 +433,12 @@ static void update_mba_bw(struct rdtgroup *rgrp, struct rdt_domain *dom_mbm)
        }
 }
 
-static void mbm_update(struct rdt_domain *d, int rmid)
+static void mbm_update(struct rdt_resource *r, struct rdt_domain *d, int rmid)
 {
        struct rmid_read rr;
 
        rr.first = false;
+       rr.r = r;
        rr.d = d;
 
        /*
@@ -510,6 +511,7 @@ void mbm_handle_overflow(struct work_struct *work)
        struct rdtgroup *prgrp, *crgrp;
        int cpu = smp_processor_id();
        struct list_head *head;
+       struct rdt_resource *r;
        struct rdt_domain *d;
 
        mutex_lock(&rdtgroup_mutex);
@@ -517,16 +519,18 @@ void mbm_handle_overflow(struct work_struct *work)
        if (!static_branch_likely(&rdt_mon_enable_key))
                goto out_unlock;
 
-       d = get_domain_from_cpu(cpu, &rdt_resources_all[RDT_RESOURCE_L3]);
+       r = &rdt_resources_all[RDT_RESOURCE_L3];
+
+       d = get_domain_from_cpu(cpu, r);
        if (!d)
                goto out_unlock;
 
        list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) {
-               mbm_update(d, prgrp->mon.rmid);
+               mbm_update(r, d, prgrp->mon.rmid);
 
                head = &prgrp->mon.crdtgrp_list;
                list_for_each_entry(crgrp, head, mon.crdtgrp_list)
-                       mbm_update(d, crgrp->mon.rmid);
+                       mbm_update(r, d, crgrp->mon.rmid);
 
                if (is_mba_sc(NULL))
                        update_mba_bw(prgrp, d);
@@ -614,11 +618,18 @@ static void l3_mon_evt_init(struct rdt_resource *r)
 
 int rdt_get_mon_l3_config(struct rdt_resource *r)
 {
+       unsigned int mbm_offset = boot_cpu_data.x86_cache_mbm_width_offset;
        unsigned int cl_size = boot_cpu_data.x86_cache_size;
        int ret;
 
        r->mon_scale = boot_cpu_data.x86_cache_occ_scale;
        r->num_rmid = boot_cpu_data.x86_cache_max_rmid + 1;
+       r->mbm_width = MBM_CNTR_WIDTH_BASE;
+
+       if (mbm_offset > 0 && mbm_offset <= MBM_CNTR_WIDTH_OFFSET_MAX)
+               r->mbm_width += mbm_offset;
+       else if (mbm_offset > MBM_CNTR_WIDTH_OFFSET_MAX)
+               pr_warn("Ignoring impossible MBM counter offset\n");
 
        /*
         * A reasonable upper limit on the max threshold is the number
index d7623e1b927d1d9fbdc53c59dd8ed4c06c9e64dd..4bd28b388a1aa77af2b3fca81a27b4e5f1ce8173 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <asm/cacheflush.h>
 #include <asm/intel-family.h>
-#include <asm/resctrl_sched.h>
+#include <asm/resctrl.h>
 #include <asm/perf_event.h>
 
 #include "../../events/perf_event.h" /* For X86_CONFIG() */
index 5a359d9fcc055701956d36a4857f15853702e1c6..d7cb5ab0d1f0997457833a140f42aae21d779f53 100644 (file)
@@ -29,7 +29,7 @@
 
 #include <uapi/linux/magic.h>
 
-#include <asm/resctrl_sched.h>
+#include <asm/resctrl.h>
 #include "internal.h"
 
 DEFINE_STATIC_KEY_FALSE(rdt_enable_key);
@@ -2472,7 +2472,7 @@ static int mkdir_mondata_subdir(struct kernfs_node *parent_kn,
                        goto out_destroy;
 
                if (is_mbm_event(mevt->evtid))
-                       mon_event_read(&rr, d, prgrp, mevt->evtid, true);
+                       mon_event_read(&rr, r, d, prgrp, mevt->evtid, true);
        }
        kernfs_activate(kn);
        return 0;
index 8e3a8fedfa4d705086376671d63d081898df971c..722fd712e1cf029adff9a73c73aad44992dbcef6 100644 (file)
@@ -87,7 +87,6 @@ static bool in_softirq_stack(unsigned long *stack, struct stack_info *info)
 
 static bool in_doublefault_stack(unsigned long *stack, struct stack_info *info)
 {
-#ifdef CONFIG_DOUBLEFAULT
        struct cpu_entry_area *cea = get_cpu_entry_area(raw_smp_processor_id());
        struct doublefault_stack *ss = &cea->doublefault_stack;
 
@@ -103,9 +102,6 @@ static bool in_doublefault_stack(unsigned long *stack, struct stack_info *info)
        info->next_sp   = (unsigned long *)this_cpu_read(cpu_tss_rw.x86_tss.sp);
 
        return true;
-#else
-       return false;
-#endif
 }
 
 
index c5399e80c59c5d2a88e1b62f4bdc8bba43be6991..4d13c57f370a9c97b4ff6f73d9a74a124a869805 100644 (file)
@@ -910,14 +910,6 @@ static int __init parse_memmap_one(char *p)
                return -EINVAL;
 
        if (!strncmp(p, "exactmap", 8)) {
-#ifdef CONFIG_CRASH_DUMP
-               /*
-                * If we are doing a crash dump, we still need to know
-                * the real memory size before the original memory map is
-                * reset.
-                */
-               saved_max_pfn = e820__end_of_ram_pfn();
-#endif
                e820_table->nr_entries = 0;
                userdef = 1;
                return 0;
index 9b33904251a9f58e0fe7e1fd7565fb5437df7259..93fbdff2974f9194766429d83830cf24d62751ad 100644 (file)
 #include <xen/hvc-console.h>
 #include <asm/pci-direct.h>
 #include <asm/fixmap.h>
-#include <asm/intel-mid.h>
 #include <asm/pgtable.h>
 #include <linux/usb/ehci_def.h>
 #include <linux/usb/xhci-dbgp.h>
-#include <linux/efi.h>
-#include <asm/efi.h>
 #include <asm/pci_x86.h>
 
 /* Simple VGA output */
index 12c70840980e4b8aacf26541ff3814cbc4ebaf11..06c818967bb63765aa10be6b7ce7033f5c055282 100644 (file)
@@ -291,15 +291,13 @@ void fpu__drop(struct fpu *fpu)
 }
 
 /*
- * Clear FPU registers by setting them up from
- * the init fpstate:
+ * Clear FPU registers by setting them up from the init fpstate.
+ * Caller must do fpregs_[un]lock() around it.
  */
-static inline void copy_init_fpstate_to_fpregs(void)
+static inline void copy_init_fpstate_to_fpregs(u64 features_mask)
 {
-       fpregs_lock();
-
        if (use_xsave())
-               copy_kernel_to_xregs(&init_fpstate.xsave, -1);
+               copy_kernel_to_xregs(&init_fpstate.xsave, features_mask);
        else if (static_cpu_has(X86_FEATURE_FXSR))
                copy_kernel_to_fxregs(&init_fpstate.fxsave);
        else
@@ -307,9 +305,6 @@ static inline void copy_init_fpstate_to_fpregs(void)
 
        if (boot_cpu_has(X86_FEATURE_OSPKE))
                copy_init_pkru_to_fpregs();
-
-       fpregs_mark_activate();
-       fpregs_unlock();
 }
 
 /*
@@ -318,18 +313,40 @@ static inline void copy_init_fpstate_to_fpregs(void)
  * Called by sys_execve(), by the signal handler code and by various
  * error paths.
  */
-void fpu__clear(struct fpu *fpu)
+static void fpu__clear(struct fpu *fpu, bool user_only)
 {
-       WARN_ON_FPU(fpu != &current->thread.fpu); /* Almost certainly an anomaly */
+       WARN_ON_FPU(fpu != &current->thread.fpu);
 
-       fpu__drop(fpu);
+       if (!static_cpu_has(X86_FEATURE_FPU)) {
+               fpu__drop(fpu);
+               fpu__initialize(fpu);
+               return;
+       }
 
-       /*
-        * Make sure fpstate is cleared and initialized.
-        */
-       fpu__initialize(fpu);
-       if (static_cpu_has(X86_FEATURE_FPU))
-               copy_init_fpstate_to_fpregs();
+       fpregs_lock();
+
+       if (user_only) {
+               if (!fpregs_state_valid(fpu, smp_processor_id()) &&
+                   xfeatures_mask_supervisor())
+                       copy_kernel_to_xregs(&fpu->state.xsave,
+                                            xfeatures_mask_supervisor());
+               copy_init_fpstate_to_fpregs(xfeatures_mask_user());
+       } else {
+               copy_init_fpstate_to_fpregs(xfeatures_mask_all);
+       }
+
+       fpregs_mark_activate();
+       fpregs_unlock();
+}
+
+void fpu__clear_user_states(struct fpu *fpu)
+{
+       fpu__clear(fpu, true);
+}
+
+void fpu__clear_all(struct fpu *fpu)
+{
+       fpu__clear(fpu, false);
 }
 
 /*
index 6ce7e0a23268fdb20f5b5e43e16b40eb7d0e7561..61ddc3a5e5c2b659296b7720350d058e6ea009fc 100644 (file)
@@ -224,7 +224,8 @@ static void __init fpu__init_system_xstate_size_legacy(void)
  */
 u64 __init fpu__get_supported_xfeatures_mask(void)
 {
-       return XCNTXT_MASK;
+       return XFEATURE_MASK_USER_SUPPORTED |
+              XFEATURE_MASK_SUPERVISOR_SUPPORTED;
 }
 
 /* Legacy code to initialize eager fpu mode. */
index d652b939ccfb5865f8a8383e04e71adfe450c1e1..bd1d0649f8cef6ae5fc6d991f92f53c9d75a94e2 100644 (file)
@@ -139,7 +139,7 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
        } else {
                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
                if (!ret)
-                       ret = validate_xstate_header(&xsave->header);
+                       ret = validate_user_xstate_header(&xsave->header);
        }
 
        /*
index 400a05e1c1c519988a348be099c1160f68b0cbd8..9393a445d73c81f97b912969fc6a6ce2b9952e27 100644 (file)
@@ -211,9 +211,9 @@ retry:
 }
 
 static inline void
-sanitize_restored_xstate(union fpregs_state *state,
-                        struct user_i387_ia32_struct *ia32_env,
-                        u64 xfeatures, int fx_only)
+sanitize_restored_user_xstate(union fpregs_state *state,
+                             struct user_i387_ia32_struct *ia32_env,
+                             u64 user_xfeatures, int fx_only)
 {
        struct xregs_state *xsave = &state->xsave;
        struct xstate_header *header = &xsave->header;
@@ -226,13 +226,22 @@ sanitize_restored_xstate(union fpregs_state *state,
                 */
 
                /*
-                * Init the state that is not present in the memory
-                * layout and not enabled by the OS.
+                * 'user_xfeatures' might have bits clear which are
+                * set in header->xfeatures. This represents features that
+                * were in init state prior to a signal delivery, and need
+                * to be reset back to the init state.  Clear any user
+                * feature bits which are set in the kernel buffer to get
+                * them back to the init state.
+                *
+                * Supervisor state is unchanged by input from userspace.
+                * Ensure supervisor state bits stay set and supervisor
+                * state is not modified.
                 */
                if (fx_only)
                        header->xfeatures = XFEATURE_MASK_FPSSE;
                else
-                       header->xfeatures &= xfeatures;
+                       header->xfeatures &= user_xfeatures |
+                                            xfeatures_mask_supervisor();
        }
 
        if (use_fxsr()) {
@@ -252,16 +261,24 @@ sanitize_restored_xstate(union fpregs_state *state,
  */
 static int copy_user_to_fpregs_zeroing(void __user *buf, u64 xbv, int fx_only)
 {
+       u64 init_bv;
+       int r;
+
        if (use_xsave()) {
                if (fx_only) {
-                       u64 init_bv = xfeatures_mask & ~XFEATURE_MASK_FPSSE;
-                       copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
-                       return copy_user_to_fxregs(buf);
+                       init_bv = xfeatures_mask_user() & ~XFEATURE_MASK_FPSSE;
+
+                       r = copy_user_to_fxregs(buf);
+                       if (!r)
+                               copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
+                       return r;
                } else {
-                       u64 init_bv = xfeatures_mask & ~xbv;
-                       if (unlikely(init_bv))
+                       init_bv = xfeatures_mask_user() & ~xbv;
+
+                       r = copy_user_to_xregs(buf, xbv);
+                       if (!r && unlikely(init_bv))
                                copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
-                       return copy_user_to_xregs(buf, xbv);
+                       return r;
                }
        } else if (use_fxsr()) {
                return copy_user_to_fxregs(buf);
@@ -277,7 +294,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
        struct task_struct *tsk = current;
        struct fpu *fpu = &tsk->thread.fpu;
        struct user_i387_ia32_struct env;
-       u64 xfeatures = 0;
+       u64 user_xfeatures = 0;
        int fx_only = 0;
        int ret = 0;
 
@@ -285,7 +302,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                         IS_ENABLED(CONFIG_IA32_EMULATION));
 
        if (!buf) {
-               fpu__clear(fpu);
+               fpu__clear_user_states(fpu);
                return 0;
        }
 
@@ -310,32 +327,14 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                        trace_x86_fpu_xstate_check_failed(fpu);
                } else {
                        state_size = fx_sw_user.xstate_size;
-                       xfeatures = fx_sw_user.xfeatures;
+                       user_xfeatures = fx_sw_user.xfeatures;
                }
        }
 
-       /*
-        * The current state of the FPU registers does not matter. By setting
-        * TIF_NEED_FPU_LOAD unconditionally it is ensured that the our xstate
-        * is not modified on context switch and that the xstate is considered
-        * to be loaded again on return to userland (overriding last_cpu avoids
-        * the optimisation).
-        */
-       set_thread_flag(TIF_NEED_FPU_LOAD);
-       __fpu_invalidate_fpregs_state(fpu);
-
        if ((unsigned long)buf_fx % 64)
                fx_only = 1;
-       /*
-        * For 32-bit frames with fxstate, copy the fxstate so it can be
-        * reconstructed later.
-        */
-       if (ia32_fxstate) {
-               ret = __copy_from_user(&env, buf, sizeof(env));
-               if (ret)
-                       goto err_out;
-               envp = &env;
-       } else {
+
+       if (!ia32_fxstate) {
                /*
                 * Attempt to restore the FPU registers directly from user
                 * memory. For that to succeed, the user access cannot cause
@@ -345,20 +344,65 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                 */
                fpregs_lock();
                pagefault_disable();
-               ret = copy_user_to_fpregs_zeroing(buf_fx, xfeatures, fx_only);
+               ret = copy_user_to_fpregs_zeroing(buf_fx, user_xfeatures, fx_only);
                pagefault_enable();
                if (!ret) {
+
+                       /*
+                        * Restore supervisor states: previous context switch
+                        * etc has done XSAVES and saved the supervisor states
+                        * in the kernel buffer from which they can be restored
+                        * now.
+                        *
+                        * We cannot do a single XRSTORS here - which would
+                        * be nice - because the rest of the FPU registers are
+                        * being restored from a user buffer directly. The
+                        * single XRSTORS happens below, when the user buffer
+                        * has been copied to the kernel one.
+                        */
+                       if (test_thread_flag(TIF_NEED_FPU_LOAD) &&
+                           xfeatures_mask_supervisor())
+                               copy_kernel_to_xregs(&fpu->state.xsave,
+                                                    xfeatures_mask_supervisor());
                        fpregs_mark_activate();
                        fpregs_unlock();
                        return 0;
                }
-               fpregs_deactivate(fpu);
                fpregs_unlock();
+       } else {
+               /*
+                * For 32-bit frames with fxstate, copy the fxstate so it can
+                * be reconstructed later.
+                */
+               ret = __copy_from_user(&env, buf, sizeof(env));
+               if (ret)
+                       goto err_out;
+               envp = &env;
        }
 
+       /*
+        * By setting TIF_NEED_FPU_LOAD it is ensured that our xstate is
+        * not modified on context switch and that the xstate is considered
+        * to be loaded again on return to userland (overriding last_cpu avoids
+        * the optimisation).
+        */
+       fpregs_lock();
+
+       if (!test_thread_flag(TIF_NEED_FPU_LOAD)) {
+
+               /*
+                * Supervisor states are not modified by user space input.  Save
+                * current supervisor states first and invalidate the FPU regs.
+                */
+               if (xfeatures_mask_supervisor())
+                       copy_supervisor_to_kernel(&fpu->state.xsave);
+               set_thread_flag(TIF_NEED_FPU_LOAD);
+       }
+       __fpu_invalidate_fpregs_state(fpu);
+       fpregs_unlock();
 
        if (use_xsave() && !fx_only) {
-               u64 init_bv = xfeatures_mask & ~xfeatures;
+               u64 init_bv = xfeatures_mask_user() & ~user_xfeatures;
 
                if (using_compacted_format()) {
                        ret = copy_user_to_xstate(&fpu->state.xsave, buf_fx);
@@ -366,17 +410,24 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                        ret = __copy_from_user(&fpu->state.xsave, buf_fx, state_size);
 
                        if (!ret && state_size > offsetof(struct xregs_state, header))
-                               ret = validate_xstate_header(&fpu->state.xsave.header);
+                               ret = validate_user_xstate_header(&fpu->state.xsave.header);
                }
                if (ret)
                        goto err_out;
 
-               sanitize_restored_xstate(&fpu->state, envp, xfeatures, fx_only);
+               sanitize_restored_user_xstate(&fpu->state, envp, user_xfeatures,
+                                             fx_only);
 
                fpregs_lock();
                if (unlikely(init_bv))
                        copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
-               ret = copy_kernel_to_xregs_err(&fpu->state.xsave, xfeatures);
+
+               /*
+                * Restore previously saved supervisor xstates along with
+                * copied-in user xstates.
+                */
+               ret = copy_kernel_to_xregs_err(&fpu->state.xsave,
+                                              user_xfeatures | xfeatures_mask_supervisor());
 
        } else if (use_fxsr()) {
                ret = __copy_from_user(&fpu->state.fxsave, buf_fx, state_size);
@@ -385,11 +436,14 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
                        goto err_out;
                }
 
-               sanitize_restored_xstate(&fpu->state, envp, xfeatures, fx_only);
+               sanitize_restored_user_xstate(&fpu->state, envp, user_xfeatures,
+                                             fx_only);
 
                fpregs_lock();
                if (use_xsave()) {
-                       u64 init_bv = xfeatures_mask & ~XFEATURE_MASK_FPSSE;
+                       u64 init_bv;
+
+                       init_bv = xfeatures_mask_user() & ~XFEATURE_MASK_FPSSE;
                        copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
                }
 
@@ -410,7 +464,7 @@ static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
 
 err_out:
        if (ret)
-               fpu__clear(fpu);
+               fpu__clear_user_states(fpu);
        return ret;
 }
 
@@ -465,7 +519,7 @@ void fpu__init_prepare_fx_sw_frame(void)
 
        fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
        fx_sw_reserved.extended_size = size;
-       fx_sw_reserved.xfeatures = xfeatures_mask;
+       fx_sw_reserved.xfeatures = xfeatures_mask_user();
        fx_sw_reserved.xstate_size = fpu_user_xstate_size;
 
        if (IS_ENABLED(CONFIG_IA32_EMULATION) ||
index 32b153d387486835a4814c62d76eed6cc7a37dac..bda2e5eaca0e0c5eb618f0a48ba3b064eb1fc412 100644 (file)
@@ -54,13 +54,15 @@ static short xsave_cpuid_features[] __initdata = {
 };
 
 /*
- * Mask of xstate features supported by the CPU and the kernel:
+ * This represents the full set of bits that should ever be set in a kernel
+ * XSAVE buffer, both supervisor and user xstates.
  */
-u64 xfeatures_mask __read_mostly;
+u64 xfeatures_mask_all __read_mostly;
 
 static unsigned int xstate_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
 static unsigned int xstate_sizes[XFEATURE_MAX]   = { [ 0 ... XFEATURE_MAX - 1] = -1};
 static unsigned int xstate_comp_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
+static unsigned int xstate_supervisor_only_offsets[XFEATURE_MAX] = { [ 0 ... XFEATURE_MAX - 1] = -1};
 
 /*
  * The XSAVE area of kernel can be in standard or compacted format;
@@ -76,7 +78,7 @@ unsigned int fpu_user_xstate_size;
  */
 int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_name)
 {
-       u64 xfeatures_missing = xfeatures_needed & ~xfeatures_mask;
+       u64 xfeatures_missing = xfeatures_needed & ~xfeatures_mask_all;
 
        if (unlikely(feature_name)) {
                long xfeature_idx, max_idx;
@@ -150,7 +152,7 @@ void fpstate_sanitize_xstate(struct fpu *fpu)
         * None of the feature bits are in init state. So nothing else
         * to do for us, as the memory layout is up to date.
         */
-       if ((xfeatures & xfeatures_mask) == xfeatures_mask)
+       if ((xfeatures & xfeatures_mask_all) == xfeatures_mask_all)
                return;
 
        /*
@@ -177,7 +179,7 @@ void fpstate_sanitize_xstate(struct fpu *fpu)
         * in a special way already:
         */
        feature_bit = 0x2;
-       xfeatures = (xfeatures_mask & ~xfeatures) >> 2;
+       xfeatures = (xfeatures_mask_user() & ~xfeatures) >> 2;
 
        /*
         * Update all the remaining memory layouts according to their
@@ -205,30 +207,39 @@ void fpstate_sanitize_xstate(struct fpu *fpu)
  */
 void fpu__init_cpu_xstate(void)
 {
-       if (!boot_cpu_has(X86_FEATURE_XSAVE) || !xfeatures_mask)
+       u64 unsup_bits;
+
+       if (!boot_cpu_has(X86_FEATURE_XSAVE) || !xfeatures_mask_all)
                return;
        /*
-        * Make it clear that XSAVES supervisor states are not yet
-        * implemented should anyone expect it to work by changing
-        * bits in XFEATURE_MASK_* macros and XCR0.
+        * Unsupported supervisor xstates should not be found in
+        * the xfeatures mask.
         */
-       WARN_ONCE((xfeatures_mask & XFEATURE_MASK_SUPERVISOR),
-               "x86/fpu: XSAVES supervisor states are not yet implemented.\n");
+       unsup_bits = xfeatures_mask_all & XFEATURE_MASK_SUPERVISOR_UNSUPPORTED;
+       WARN_ONCE(unsup_bits, "x86/fpu: Found unsupported supervisor xstates: 0x%llx\n",
+                 unsup_bits);
 
-       xfeatures_mask &= ~XFEATURE_MASK_SUPERVISOR;
+       xfeatures_mask_all &= ~XFEATURE_MASK_SUPERVISOR_UNSUPPORTED;
 
        cr4_set_bits(X86_CR4_OSXSAVE);
-       xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
+
+       /*
+        * XCR_XFEATURE_ENABLED_MASK (aka. XCR0) sets user features
+        * managed by XSAVE{C, OPT, S} and XRSTOR{S}.  Only XSAVE user
+        * states can be set here.
+        */
+       xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask_user());
+
+       /*
+        * MSR_IA32_XSS sets supervisor states managed by XSAVES.
+        */
+       if (boot_cpu_has(X86_FEATURE_XSAVES))
+               wrmsrl(MSR_IA32_XSS, xfeatures_mask_supervisor());
 }
 
-/*
- * Note that in the future we will likely need a pair of
- * functions here: one for user xstates and the other for
- * system xstates.  For now, they are the same.
- */
-static int xfeature_enabled(enum xfeature xfeature)
+static bool xfeature_enabled(enum xfeature xfeature)
 {
-       return !!(xfeatures_mask & (1UL << xfeature));
+       return xfeatures_mask_all & BIT_ULL(xfeature);
 }
 
 /*
@@ -382,6 +393,33 @@ static void __init setup_xstate_comp_offsets(void)
        }
 }
 
+/*
+ * Setup offsets of a supervisor-state-only XSAVES buffer:
+ *
+ * The offsets stored in xstate_comp_offsets[] only work for one specific
+ * value of the Requested Feature BitMap (RFBM).  In cases where a different
+ * RFBM value is used, a different set of offsets is required.  This set of
+ * offsets is for when RFBM=xfeatures_mask_supervisor().
+ */
+static void __init setup_supervisor_only_offsets(void)
+{
+       unsigned int next_offset;
+       int i;
+
+       next_offset = FXSAVE_SIZE + XSAVE_HDR_SIZE;
+
+       for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
+               if (!xfeature_enabled(i) || !xfeature_is_supervisor(i))
+                       continue;
+
+               if (xfeature_is_aligned(i))
+                       next_offset = ALIGN(next_offset, 64);
+
+               xstate_supervisor_only_offsets[i] = next_offset;
+               next_offset += xstate_sizes[i];
+       }
+}
+
 /*
  * Print out xstate component offsets and sizes
  */
@@ -415,7 +453,7 @@ static void __init setup_init_fpu_buf(void)
 
        if (boot_cpu_has(X86_FEATURE_XSAVES))
                init_fpstate.xsave.header.xcomp_bv = XCOMP_BV_COMPACTED_FORMAT |
-                                                    xfeatures_mask;
+                                                    xfeatures_mask_all;
 
        /*
         * Init all the features state with header.xfeatures being 0x0
@@ -438,7 +476,7 @@ static int xfeature_uncompacted_offset(int xfeature_nr)
         * format. Checking a supervisor state's uncompacted offset is
         * an error.
         */
-       if (XFEATURE_MASK_SUPERVISOR & BIT_ULL(xfeature_nr)) {
+       if (XFEATURE_MASK_SUPERVISOR_ALL & BIT_ULL(xfeature_nr)) {
                WARN_ONCE(1, "No fixed offset for xstate %d\n", xfeature_nr);
                return -1;
        }
@@ -472,10 +510,10 @@ int using_compacted_format(void)
 }
 
 /* Validate an xstate header supplied by userspace (ptrace or sigreturn) */
-int validate_xstate_header(const struct xstate_header *hdr)
+int validate_user_xstate_header(const struct xstate_header *hdr)
 {
        /* No unknown or supervisor features may be set */
-       if (hdr->xfeatures & (~xfeatures_mask | XFEATURE_MASK_SUPERVISOR))
+       if (hdr->xfeatures & ~xfeatures_mask_user())
                return -EINVAL;
 
        /* Userspace must use the uncompacted format */
@@ -610,15 +648,12 @@ static void do_extra_xstate_size_checks(void)
 
 
 /*
- * Get total size of enabled xstates in XCR0/xfeatures_mask.
+ * Get total size of enabled xstates in XCR0 | IA32_XSS.
  *
  * Note the SDM's wording here.  "sub-function 0" only enumerates
  * the size of the *user* states.  If we use it to size a buffer
  * that we use 'XSAVES' on, we could potentially overflow the
  * buffer because 'XSAVES' saves system states too.
- *
- * Note that we do not currently set any bits on IA32_XSS so
- * 'XCR0 | IA32_XSS == XCR0' for now.
  */
 static unsigned int __init get_xsaves_size(void)
 {
@@ -700,7 +735,7 @@ static int __init init_xstate_size(void)
  */
 static void fpu__init_disable_system_xstate(void)
 {
-       xfeatures_mask = 0;
+       xfeatures_mask_all = 0;
        cr4_clear_bits(X86_CR4_OSXSAVE);
        setup_clear_cpu_cap(X86_FEATURE_XSAVE);
 }
@@ -735,16 +770,26 @@ void __init fpu__init_system_xstate(void)
                return;
        }
 
+       /*
+        * Find user xstates supported by the processor.
+        */
        cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
-       xfeatures_mask = eax + ((u64)edx << 32);
+       xfeatures_mask_all = eax + ((u64)edx << 32);
 
-       if ((xfeatures_mask & XFEATURE_MASK_FPSSE) != XFEATURE_MASK_FPSSE) {
+       /*
+        * Find supervisor xstates supported by the processor.
+        */
+       cpuid_count(XSTATE_CPUID, 1, &eax, &ebx, &ecx, &edx);
+       xfeatures_mask_all |= ecx + ((u64)edx << 32);
+
+       if ((xfeatures_mask_user() & XFEATURE_MASK_FPSSE) != XFEATURE_MASK_FPSSE) {
                /*
                 * This indicates that something really unexpected happened
                 * with the enumeration.  Disable XSAVE and try to continue
                 * booting without it.  This is too early to BUG().
                 */
-               pr_err("x86/fpu: FP/SSE not present amongst the CPU's xstate features: 0x%llx.\n", xfeatures_mask);
+               pr_err("x86/fpu: FP/SSE not present amongst the CPU's xstate features: 0x%llx.\n",
+                      xfeatures_mask_all);
                goto out_disable;
        }
 
@@ -753,10 +798,10 @@ void __init fpu__init_system_xstate(void)
         */
        for (i = 0; i < ARRAY_SIZE(xsave_cpuid_features); i++) {
                if (!boot_cpu_has(xsave_cpuid_features[i]))
-                       xfeatures_mask &= ~BIT(i);
+                       xfeatures_mask_all &= ~BIT_ULL(i);
        }
 
-       xfeatures_mask &= fpu__get_supported_xfeatures_mask();
+       xfeatures_mask_all &= fpu__get_supported_xfeatures_mask();
 
        /* Enable xstate instructions to be able to continue with initialization: */
        fpu__init_cpu_xstate();
@@ -768,15 +813,16 @@ void __init fpu__init_system_xstate(void)
         * Update info used for ptrace frames; use standard-format size and no
         * supervisor xstates:
         */
-       update_regset_xstate_info(fpu_user_xstate_size, xfeatures_mask & ~XFEATURE_MASK_SUPERVISOR);
+       update_regset_xstate_info(fpu_user_xstate_size, xfeatures_mask_user());
 
        fpu__init_prepare_fx_sw_frame();
        setup_init_fpu_buf();
        setup_xstate_comp_offsets();
+       setup_supervisor_only_offsets();
        print_xstate_offset_size();
 
        pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is %d bytes, using '%s' format.\n",
-               xfeatures_mask,
+               xfeatures_mask_all,
                fpu_kernel_xstate_size,
                boot_cpu_has(X86_FEATURE_XSAVES) ? "compacted" : "standard");
        return;
@@ -795,7 +841,14 @@ void fpu__resume_cpu(void)
         * Restore XCR0 on xsave capable CPUs:
         */
        if (boot_cpu_has(X86_FEATURE_XSAVE))
-               xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
+               xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask_user());
+
+       /*
+        * Restore IA32_XSS. The same CPUID bit enumerates support
+        * of XSAVES and MSR_IA32_XSS.
+        */
+       if (boot_cpu_has(X86_FEATURE_XSAVES))
+               wrmsrl(MSR_IA32_XSS, xfeatures_mask_supervisor());
 }
 
 /*
@@ -840,10 +893,9 @@ void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr)
 
        /*
         * We should not ever be requesting features that we
-        * have not enabled.  Remember that xfeatures_mask is
-        * what we write to the XCR0 register.
+        * have not enabled.
         */
-       WARN_ONCE(!(xfeatures_mask & BIT_ULL(xfeature_nr)),
+       WARN_ONCE(!(xfeatures_mask_all & BIT_ULL(xfeature_nr)),
                  "get of unsupported state");
        /*
         * This assumes the last 'xsave*' instruction to
@@ -957,18 +1009,31 @@ static inline bool xfeatures_mxcsr_quirk(u64 xfeatures)
        return true;
 }
 
-/*
- * This is similar to user_regset_copyout(), but will not add offset to
- * the source data pointer or increment pos, count, kbuf, and ubuf.
- */
-static inline void
-__copy_xstate_to_kernel(void *kbuf, const void *data,
-                       unsigned int offset, unsigned int size, unsigned int size_total)
+static void fill_gap(unsigned to, void **kbuf, unsigned *pos, unsigned *count)
 {
-       if (offset < size_total) {
-               unsigned int copy = min(size, size_total - offset);
+       if (*pos < to) {
+               unsigned size = to - *pos;
+
+               if (size > *count)
+                       size = *count;
+               memcpy(*kbuf, (void *)&init_fpstate.xsave + *pos, size);
+               *kbuf += size;
+               *pos += size;
+               *count -= size;
+       }
+}
 
-               memcpy(kbuf + offset, data, copy);
+static void copy_part(unsigned offset, unsigned size, void *from,
+                       void **kbuf, unsigned *pos, unsigned *count)
+{
+       fill_gap(offset, kbuf, pos, count);
+       if (size > *count)
+               size = *count;
+       if (size) {
+               memcpy(*kbuf, from, size);
+               *kbuf += size;
+               *pos += size;
+               *count -= size;
        }
 }
 
@@ -981,8 +1046,9 @@ __copy_xstate_to_kernel(void *kbuf, const void *data,
  */
 int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int offset_start, unsigned int size_total)
 {
-       unsigned int offset, size;
        struct xstate_header header;
+       const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr);
+       unsigned count = size_total;
        int i;
 
        /*
@@ -996,48 +1062,44 @@ int copy_xstate_to_kernel(void *kbuf, struct xregs_state *xsave, unsigned int of
         */
        memset(&header, 0, sizeof(header));
        header.xfeatures = xsave->header.xfeatures;
-       header.xfeatures &= ~XFEATURE_MASK_SUPERVISOR;
-
+       header.xfeatures &= xfeatures_mask_user();
+
+       if (header.xfeatures & XFEATURE_MASK_FP)
+               copy_part(0, off_mxcsr,
+                         &xsave->i387, &kbuf, &offset_start, &count);
+       if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM))
+               copy_part(off_mxcsr, MXCSR_AND_FLAGS_SIZE,
+                         &xsave->i387.mxcsr, &kbuf, &offset_start, &count);
+       if (header.xfeatures & XFEATURE_MASK_FP)
+               copy_part(offsetof(struct fxregs_state, st_space), 128,
+                         &xsave->i387.st_space, &kbuf, &offset_start, &count);
+       if (header.xfeatures & XFEATURE_MASK_SSE)
+               copy_part(xstate_offsets[XFEATURE_MASK_SSE], 256,
+                         &xsave->i387.xmm_space, &kbuf, &offset_start, &count);
+       /*
+        * Fill xsave->i387.sw_reserved value for ptrace frame:
+        */
+       copy_part(offsetof(struct fxregs_state, sw_reserved), 48,
+                 xstate_fx_sw_bytes, &kbuf, &offset_start, &count);
        /*
         * Copy xregs_state->header:
         */
-       offset = offsetof(struct xregs_state, header);
-       size = sizeof(header);
+       copy_part(offsetof(struct xregs_state, header), sizeof(header),
+                 &header, &kbuf, &offset_start, &count);
 
-       __copy_xstate_to_kernel(kbuf, &header, offset, size, size_total);
-
-       for (i = 0; i < XFEATURE_MAX; i++) {
+       for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) {
                /*
                 * Copy only in-use xstates:
                 */
                if ((header.xfeatures >> i) & 1) {
                        void *src = __raw_xsave_addr(xsave, i);
 
-                       offset = xstate_offsets[i];
-                       size = xstate_sizes[i];
-
-                       /* The next component has to fit fully into the output buffer: */
-                       if (offset + size > size_total)
-                               break;
-
-                       __copy_xstate_to_kernel(kbuf, src, offset, size, size_total);
+                       copy_part(xstate_offsets[i], xstate_sizes[i],
+                                 src, &kbuf, &offset_start, &count);
                }
 
        }
-
-       if (xfeatures_mxcsr_quirk(header.xfeatures)) {
-               offset = offsetof(struct fxregs_state, mxcsr);
-               size = MXCSR_AND_FLAGS_SIZE;
-               __copy_xstate_to_kernel(kbuf, &xsave->i387.mxcsr, offset, size, size_total);
-       }
-
-       /*
-        * Fill xsave->i387.sw_reserved value for ptrace frame:
-        */
-       offset = offsetof(struct fxregs_state, sw_reserved);
-       size = sizeof(xstate_fx_sw_bytes);
-
-       __copy_xstate_to_kernel(kbuf, xstate_fx_sw_bytes, offset, size, size_total);
+       fill_gap(size_total, &kbuf, &offset_start, &count);
 
        return 0;
 }
@@ -1080,7 +1142,7 @@ int copy_xstate_to_user(void __user *ubuf, struct xregs_state *xsave, unsigned i
         */
        memset(&header, 0, sizeof(header));
        header.xfeatures = xsave->header.xfeatures;
-       header.xfeatures &= ~XFEATURE_MASK_SUPERVISOR;
+       header.xfeatures &= xfeatures_mask_user();
 
        /*
         * Copy xregs_state->header:
@@ -1147,7 +1209,7 @@ int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf)
 
        memcpy(&hdr, kbuf + offset, size);
 
-       if (validate_xstate_header(&hdr))
+       if (validate_user_xstate_header(&hdr))
                return -EINVAL;
 
        for (i = 0; i < XFEATURE_MAX; i++) {
@@ -1173,7 +1235,7 @@ int copy_kernel_to_xstate(struct xregs_state *xsave, const void *kbuf)
         * The state that came in from userspace was user-state only.
         * Mask all the user states out of 'xfeatures':
         */
-       xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR;
+       xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR_ALL;
 
        /*
         * Add back in the features that came in from userspace:
@@ -1201,7 +1263,7 @@ int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf)
        if (__copy_from_user(&hdr, ubuf + offset, size))
                return -EFAULT;
 
-       if (validate_xstate_header(&hdr))
+       if (validate_user_xstate_header(&hdr))
                return -EINVAL;
 
        for (i = 0; i < XFEATURE_MAX; i++) {
@@ -1229,7 +1291,7 @@ int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf)
         * The state that came in from userspace was user-state only.
         * Mask all the user states out of 'xfeatures':
         */
-       xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR;
+       xsave->header.xfeatures &= XFEATURE_MASK_SUPERVISOR_ALL;
 
        /*
         * Add back in the features that came in from userspace:
@@ -1239,6 +1301,61 @@ int copy_user_to_xstate(struct xregs_state *xsave, const void __user *ubuf)
        return 0;
 }
 
+/*
+ * Save only supervisor states to the kernel buffer.  This blows away all
+ * old states, and is intended to be used only in __fpu__restore_sig(), where
+ * user states are restored from the user buffer.
+ */
+void copy_supervisor_to_kernel(struct xregs_state *xstate)
+{
+       struct xstate_header *header;
+       u64 max_bit, min_bit;
+       u32 lmask, hmask;
+       int err, i;
+
+       if (WARN_ON(!boot_cpu_has(X86_FEATURE_XSAVES)))
+               return;
+
+       if (!xfeatures_mask_supervisor())
+               return;
+
+       max_bit = __fls(xfeatures_mask_supervisor());
+       min_bit = __ffs(xfeatures_mask_supervisor());
+
+       lmask = xfeatures_mask_supervisor();
+       hmask = xfeatures_mask_supervisor() >> 32;
+       XSTATE_OP(XSAVES, xstate, lmask, hmask, err);
+
+       /* We should never fault when copying to a kernel buffer: */
+       if (WARN_ON_FPU(err))
+               return;
+
+       /*
+        * At this point, the buffer has only supervisor states and must be
+        * converted back to normal kernel format.
+        */
+       header = &xstate->header;
+       header->xcomp_bv |= xfeatures_mask_all;
+
+       /*
+        * This only moves states up in the buffer.  Start with
+        * the last state and move backwards so that states are
+        * not overwritten until after they are moved.  Note:
+        * memmove() allows overlapping src/dst buffers.
+        */
+       for (i = max_bit; i >= min_bit; i--) {
+               u8 *xbuf = (u8 *)xstate;
+
+               if (!((header->xfeatures >> i) & 1))
+                       continue;
+
+               /* Move xfeature 'i' into its normal location */
+               memmove(xbuf + xstate_comp_offsets[i],
+                       xbuf + xstate_supervisor_only_offsets[i],
+                       xstate_sizes[i]);
+       }
+}
+
 #ifdef CONFIG_PROC_PID_ARCH_STATUS
 /*
  * Report the amount of time elapsed in millisecond since last AVX512
index b0e641793be4fb280cec2e83d8c99ea7bd922ae3..c84d28e90a584b3ec07594041f3b981b1117187e 100644 (file)
@@ -282,7 +282,8 @@ static inline void tramp_free(void *tramp) { }
 
 /* Defined as markers to the end of the ftrace default trampolines */
 extern void ftrace_regs_caller_end(void);
-extern void ftrace_epilogue(void);
+extern void ftrace_regs_caller_ret(void);
+extern void ftrace_caller_end(void);
 extern void ftrace_caller_op_ptr(void);
 extern void ftrace_regs_caller_op_ptr(void);
 
@@ -334,7 +335,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
                call_offset = (unsigned long)ftrace_regs_call;
        } else {
                start_offset = (unsigned long)ftrace_caller;
-               end_offset = (unsigned long)ftrace_epilogue;
+               end_offset = (unsigned long)ftrace_caller_end;
                op_offset = (unsigned long)ftrace_caller_op_ptr;
                call_offset = (unsigned long)ftrace_call;
        }
@@ -366,6 +367,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
        if (WARN_ON(ret < 0))
                goto fail;
 
+       if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
+               ip = trampoline + (ftrace_regs_caller_ret - ftrace_regs_caller);
+               ret = probe_kernel_read(ip, (void *)retq, RET_SIZE);
+               if (WARN_ON(ret < 0))
+                       goto fail;
+       }
+
        /*
         * The address of the ftrace_ops that is used for this trampoline
         * is stored at the end of the trampoline. This will be used to
@@ -433,7 +441,7 @@ void set_ftrace_ops_ro(void)
                        end_offset = (unsigned long)ftrace_regs_caller_end;
                } else {
                        start_offset = (unsigned long)ftrace_caller;
-                       end_offset = (unsigned long)ftrace_epilogue;
+                       end_offset = (unsigned long)ftrace_caller_end;
                }
                size = end_offset - start_offset;
                size = size + RET_SIZE + sizeof(void *);
index e8a9f8370112de2e1ae1f4d87f5dbd717243cd62..e405fe1a8bf41931fb77b63e1a16ed5d821dc7b9 100644 (file)
@@ -189,5 +189,5 @@ return_to_handler:
        movl    %eax, %ecx
        popl    %edx
        popl    %eax
-       JMP_NOSPEC %ecx
+       JMP_NOSPEC ecx
 #endif
index 369e61faacfe39843a060e359d2c81c8ce7f27bf..aa5d28aeb31e4c21f61a644c49472ed8366cdb7e 100644 (file)
@@ -23,7 +23,7 @@
 #endif /* CONFIG_FRAME_POINTER */
 
 /* Size of stack used to save mcount regs in save_mcount_regs */
-#define MCOUNT_REG_SIZE                (SS+8 + MCOUNT_FRAME_SIZE)
+#define MCOUNT_REG_SIZE                (FRAME_SIZE + MCOUNT_FRAME_SIZE)
 
 /*
  * gcc -pg option adds a call to 'mcount' in most functions.
@@ -77,7 +77,7 @@
        /*
         * We add enough stack to save all regs.
         */
-       subq $(MCOUNT_REG_SIZE - MCOUNT_FRAME_SIZE), %rsp
+       subq $(FRAME_SIZE), %rsp
        movq %rax, RAX(%rsp)
        movq %rcx, RCX(%rsp)
        movq %rdx, RDX(%rsp)
@@ -157,8 +157,12 @@ SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
         * think twice before adding any new code or changing the
         * layout here.
         */
-SYM_INNER_LABEL(ftrace_epilogue, SYM_L_GLOBAL)
+SYM_INNER_LABEL(ftrace_caller_end, SYM_L_GLOBAL)
 
+       jmp ftrace_epilogue
+SYM_FUNC_END(ftrace_caller);
+
+SYM_FUNC_START(ftrace_epilogue)
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
        jmp ftrace_stub
@@ -170,14 +174,12 @@ SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
  */
 SYM_INNER_LABEL_ALIGN(ftrace_stub, SYM_L_WEAK)
        retq
-SYM_FUNC_END(ftrace_caller)
+SYM_FUNC_END(ftrace_epilogue)
 
 SYM_FUNC_START(ftrace_regs_caller)
        /* Save the current flags before any operations that can change them */
        pushfq
 
-       UNWIND_HINT_SAVE
-
        /* added 8 bytes to save flags */
        save_mcount_regs 8
        /* save_mcount_regs fills in first two parameters */
@@ -233,10 +235,13 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
        movq ORIG_RAX(%rsp), %rax
        movq %rax, MCOUNT_REG_SIZE-8(%rsp)
 
-       /* If ORIG_RAX is anything but zero, make this a call to that */
+       /*
+        * If ORIG_RAX is anything but zero, make this a call to that.
+        * See arch_ftrace_set_direct_caller().
+        */
        movq ORIG_RAX(%rsp), %rax
-       cmpq    $0, %rax
-       je      1f
+       testq   %rax, %rax
+       jz      1f
 
        /* Swap the flags with orig_rax */
        movq MCOUNT_REG_SIZE(%rsp), %rdi
@@ -244,20 +249,14 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
        movq %rax, MCOUNT_REG_SIZE(%rsp)
 
        restore_mcount_regs 8
+       /* Restore flags */
+       popfq
 
-       jmp     2f
+SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL);
+       UNWIND_HINT_RET_OFFSET
+       jmp     ftrace_epilogue
 
 1:     restore_mcount_regs
-
-
-2:
-       /*
-        * The stack layout is nondetermistic here, depending on which path was
-        * taken.  This confuses objtool and ORC, rightfully so.  For now,
-        * pretend the stack always looks like the non-direct case.
-        */
-       UNWIND_HINT_RESTORE
-
        /* Restore flags */
        popfq
 
@@ -268,7 +267,6 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
         * to the return.
         */
 SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
-
        jmp ftrace_epilogue
 
 SYM_FUNC_END(ftrace_regs_caller)
@@ -303,7 +301,7 @@ trace:
         * function tracing is enabled.
         */
        movq ftrace_trace_function, %r8
-       CALL_NOSPEC %r8
+       CALL_NOSPEC r8
        restore_mcount_regs
 
        jmp fgraph_trace
@@ -340,6 +338,6 @@ SYM_CODE_START(return_to_handler)
        movq 8(%rsp), %rdx
        movq (%rsp), %rax
        addq $24, %rsp
-       JMP_NOSPEC %rdi
+       JMP_NOSPEC rdi
 SYM_CODE_END(return_to_handler)
 #endif
index a53e7b4a741925ba9048fcbba3030bccc9f3766e..e2fab3ceb09fb7da1d307e69af014b24ea65735f 100644 (file)
@@ -33,15 +33,15 @@ void io_bitmap_share(struct task_struct *tsk)
        set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
 }
 
-static void task_update_io_bitmap(void)
+static void task_update_io_bitmap(struct task_struct *tsk)
 {
-       struct thread_struct *t = &current->thread;
+       struct thread_struct *t = &tsk->thread;
 
        if (t->iopl_emul == 3 || t->io_bitmap) {
                /* TSS update is handled on exit to user space */
-               set_thread_flag(TIF_IO_BITMAP);
+               set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
        } else {
-               clear_thread_flag(TIF_IO_BITMAP);
+               clear_tsk_thread_flag(tsk, TIF_IO_BITMAP);
                /* Invalidate TSS */
                preempt_disable();
                tss_update_io_bitmap();
@@ -49,12 +49,12 @@ static void task_update_io_bitmap(void)
        }
 }
 
-void io_bitmap_exit(void)
+void io_bitmap_exit(struct task_struct *tsk)
 {
-       struct io_bitmap *iobm = current->thread.io_bitmap;
+       struct io_bitmap *iobm = tsk->thread.io_bitmap;
 
-       current->thread.io_bitmap = NULL;
-       task_update_io_bitmap();
+       tsk->thread.io_bitmap = NULL;
+       task_update_io_bitmap(tsk);
        if (iobm && refcount_dec_and_test(&iobm->refcnt))
                kfree(iobm);
 }
@@ -102,7 +102,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
                if (!iobm)
                        return -ENOMEM;
                refcount_set(&iobm->refcnt, 1);
-               io_bitmap_exit();
+               io_bitmap_exit(current);
        }
 
        /*
@@ -134,7 +134,7 @@ long ksys_ioperm(unsigned long from, unsigned long num, int turn_on)
        }
        /* All permissions dropped? */
        if (max_long == UINT_MAX) {
-               io_bitmap_exit();
+               io_bitmap_exit(current);
                return 0;
        }
 
@@ -192,7 +192,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level)
        }
 
        t->iopl_emul = level;
-       task_update_io_bitmap();
+       task_update_io_bitmap(current);
 
        return 0;
 }
index 12df3a4abfdd84f54aa611a72489b0dcf3bb19fd..6b32ab009c197a7d7a716adc7c18d3b639dd5b60 100644 (file)
@@ -43,7 +43,7 @@ static int map_irq_stack(unsigned int cpu)
                pages[i] = pfn_to_page(pa >> PAGE_SHIFT);
        }
 
-       va = vmap(pages, IRQ_STACK_SIZE / PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
+       va = vmap(pages, IRQ_STACK_SIZE / PAGE_SIZE, VM_MAP, PAGE_KERNEL);
        if (!va)
                return -ENOMEM;
 
index 6407ea21fa1ba096d6eec4720bc63451670bf7be..bdcc5146de96cd1faa585f7ad586393e7c7c93fa 100644 (file)
 #include <linux/atomic.h>
 #include <linux/sched/clock.h>
 
-#if defined(CONFIG_EDAC)
-#include <linux/edac.h>
-#endif
-
 #include <asm/cpu_entry_area.h>
 #include <asm/traps.h>
 #include <asm/mach_traps.h>
index 9da70b279dad8c36a68b6c4c5f2862ad394d849f..ce6cd220f72242d581a4860a67dc96207271ba95 100644 (file)
@@ -96,7 +96,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 }
 
 /*
- * Free current thread data structures etc..
+ * Free thread data structures etc..
  */
 void exit_thread(struct task_struct *tsk)
 {
@@ -104,7 +104,7 @@ void exit_thread(struct task_struct *tsk)
        struct fpu *fpu = &t->fpu;
 
        if (test_thread_flag(TIF_IO_BITMAP))
-               io_bitmap_exit();
+               io_bitmap_exit(tsk);
 
        free_vm86(t);
 
@@ -191,7 +191,7 @@ void flush_thread(void)
        flush_ptrace_hw_breakpoint(tsk);
        memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
 
-       fpu__clear(&tsk->thread.fpu);
+       fpu__clear_all(&tsk->thread.fpu);
 }
 
 void disable_TSC(void)
index 954b013cc585f1e1972396b9a57761c69b939ad5..538d4e8d6589d4cb6206df7988b3ff6e6cc84f83 100644 (file)
@@ -52,7 +52,7 @@
 #include <asm/debugreg.h>
 #include <asm/switch_to.h>
 #include <asm/vm86.h>
-#include <asm/resctrl_sched.h>
+#include <asm/resctrl.h>
 #include <asm/proto.h>
 
 #include "process.h"
index 5ef9d8f25b0e809e186d86f5cfb0daf004a2a2c7..0c169a5687e1caa56b49306b570f3cb678bc34d2 100644 (file)
@@ -52,7 +52,7 @@
 #include <asm/switch_to.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/vdso.h>
-#include <asm/resctrl_sched.h>
+#include <asm/resctrl.h>
 #include <asm/unistd.h>
 #include <asm/fsgsbase.h>
 #ifdef CONFIG_IA32_EMULATION
index 4b3fa6cd3106d44e85c6588331d2182080ca347f..a3767e74c758c0b8bb82b3d9a159c940d1b6dd6b 100644 (file)
@@ -237,6 +237,9 @@ static u64 __init get_ramdisk_image(void)
 
        ramdisk_image |= (u64)boot_params.ext_ramdisk_image << 32;
 
+       if (ramdisk_image == 0)
+               ramdisk_image = phys_initrd_start;
+
        return ramdisk_image;
 }
 static u64 __init get_ramdisk_size(void)
@@ -245,6 +248,9 @@ static u64 __init get_ramdisk_size(void)
 
        ramdisk_size |= (u64)boot_params.ext_ramdisk_size << 32;
 
+       if (ramdisk_size == 0)
+               ramdisk_size = phys_initrd_size;
+
        return ramdisk_size;
 }
 
index e6d7894ad1279823dfadc428e1bec88ef3c2d002..fd945ce78554ef741ff23e2c11d42623be01b102 100644 (file)
@@ -287,9 +287,9 @@ void __init setup_per_cpu_areas(void)
        /*
         * Sync back kernel address range again.  We already did this in
         * setup_arch(), but percpu data also needs to be available in
-        * the smpboot asm.  We can't reliably pick up percpu mappings
-        * using vmalloc_fault(), because exception dispatch needs
-        * percpu data.
+        * the smpboot asm and arch_sync_kernel_mappings() doesn't sync to
+        * swapper_pg_dir on 32-bit. The per-cpu mappings need to be available
+        * there too.
         *
         * FIXME: Can the later sync in setup_cpu_entry_areas() replace
         * this call?
index 83b74fb38c8fcb833cdb7d2778b567a985317ec4..399f97abee02e6b57debc8bcad45a60900549e63 100644 (file)
@@ -37,6 +37,7 @@
 #include <asm/vm86.h>
 
 #ifdef CONFIG_X86_64
+#include <linux/compat.h>
 #include <asm/proto.h>
 #include <asm/ia32_unistd.h>
 #endif /* CONFIG_X86_64 */
@@ -511,6 +512,31 @@ Efault:
 }
 #endif /* CONFIG_X86_32 */
 
+#ifdef CONFIG_X86_X32_ABI
+static int x32_copy_siginfo_to_user(struct compat_siginfo __user *to,
+               const struct kernel_siginfo *from)
+{
+       struct compat_siginfo new;
+
+       copy_siginfo_to_external32(&new, from);
+       if (from->si_signo == SIGCHLD) {
+               new._sifields._sigchld_x32._utime = from->si_utime;
+               new._sifields._sigchld_x32._stime = from->si_stime;
+       }
+       if (copy_to_user(to, &new, sizeof(struct compat_siginfo)))
+               return -EFAULT;
+       return 0;
+}
+
+int copy_siginfo_to_user32(struct compat_siginfo __user *to,
+                          const struct kernel_siginfo *from)
+{
+       if (in_x32_syscall())
+               return x32_copy_siginfo_to_user(to, from);
+       return __copy_siginfo_to_user32(to, from);
+}
+#endif /* CONFIG_X86_X32_ABI */
+
 static int x32_setup_rt_frame(struct ksignal *ksig,
                              compat_sigset_t *set,
                              struct pt_regs *regs)
@@ -543,7 +569,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig,
        user_access_end();
 
        if (ksig->ka.sa.sa_flags & SA_SIGINFO) {
-               if (__copy_siginfo_to_user32(&frame->info, &ksig->info, true))
+               if (x32_copy_siginfo_to_user(&frame->info, &ksig->info))
                        return -EFAULT;
        }
 
@@ -732,7 +758,7 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
                /*
                 * Ensure the signal handler starts with the new fpu state.
                 */
-               fpu__clear(fpu);
+               fpu__clear_user_states(fpu);
        }
        signal_setup_done(failed, ksig, stepping);
 }
index 2f24c334a938b3f0cbccb0de4f944f127e6e5503..2467f3dd35d3ad5858e22ecf62341fdc99660616 100644 (file)
@@ -1384,12 +1384,12 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
        speculative_store_bypass_ht_init();
 }
 
-void arch_enable_nonboot_cpus_begin(void)
+void arch_thaw_secondary_cpus_begin(void)
 {
        set_mtrr_aps_delayed_init();
 }
 
-void arch_enable_nonboot_cpus_end(void)
+void arch_thaw_secondary_cpus_end(void)
 {
        mtrr_aps_init();
 }
@@ -1857,24 +1857,25 @@ static bool slv_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq)
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
 
-#define ICPU(model) \
-       {X86_VENDOR_INTEL, 6, model, X86_FEATURE_APERFMPERF, 0}
+#define X86_MATCH(model)                                       \
+       X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6,            \
+               INTEL_FAM6_##model, X86_FEATURE_APERFMPERF, NULL)
 
 static const struct x86_cpu_id has_knl_turbo_ratio_limits[] = {
-       ICPU(INTEL_FAM6_XEON_PHI_KNL),
-       ICPU(INTEL_FAM6_XEON_PHI_KNM),
+       X86_MATCH(XEON_PHI_KNL),
+       X86_MATCH(XEON_PHI_KNM),
        {}
 };
 
 static const struct x86_cpu_id has_skx_turbo_ratio_limits[] = {
-       ICPU(INTEL_FAM6_SKYLAKE_X),
+       X86_MATCH(SKYLAKE_X),
        {}
 };
 
 static const struct x86_cpu_id has_glm_turbo_ratio_limits[] = {
-       ICPU(INTEL_FAM6_ATOM_GOLDMONT),
-       ICPU(INTEL_FAM6_ATOM_GOLDMONT_D),
-       ICPU(INTEL_FAM6_ATOM_GOLDMONT_PLUS),
+       X86_MATCH(ATOM_GOLDMONT),
+       X86_MATCH(ATOM_GOLDMONT_D),
+       X86_MATCH(ATOM_GOLDMONT_PLUS),
        {}
 };
 
index b89f6ac6a0c01f0c4fa1c8ccfa954fc6303f1e39..b2942b2dbfcfcdcdd952fd11389034f53ba52d92 100644 (file)
@@ -35,8 +35,7 @@
 #include "../realmode/rm/wakeup.h"
 
 /* Global pointer to shared data; NULL means no measured launch. */
-struct tboot *tboot __read_mostly;
-EXPORT_SYMBOL(tboot);
+static struct tboot *tboot __read_mostly;
 
 /* timeout for APs (in secs) to enter wait-for-SIPI state during shutdown */
 #define AP_WAIT_TIMEOUT                1
@@ -46,6 +45,11 @@ EXPORT_SYMBOL(tboot);
 
 static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
 
+bool tboot_enabled(void)
+{
+       return tboot != NULL;
+}
+
 void __init tboot_probe(void)
 {
        /* Look for valid page-aligned address for shared page. */
index d54cffdc7cac2b5c55b272c7df3a8756fbd024c1..428186d9de46c626c7034bf6344152c25e4f81b7 100644 (file)
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/io.h>
+#include <linux/hardirq.h>
+#include <linux/atomic.h>
+
 #include <asm/stacktrace.h>
 #include <asm/processor.h>
 #include <asm/debugreg.h>
-#include <linux/atomic.h>
 #include <asm/text-patching.h>
 #include <asm/ftrace.h>
 #include <asm/traps.h>
@@ -82,78 +84,6 @@ static inline void cond_local_irq_disable(struct pt_regs *regs)
                local_irq_disable();
 }
 
-/*
- * In IST context, we explicitly disable preemption.  This serves two
- * purposes: it makes it much less likely that we would accidentally
- * schedule in IST context and it will force a warning if we somehow
- * manage to schedule by accident.
- */
-void ist_enter(struct pt_regs *regs)
-{
-       if (user_mode(regs)) {
-               RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
-       } else {
-               /*
-                * We might have interrupted pretty much anything.  In
-                * fact, if we're a machine check, we can even interrupt
-                * NMI processing.  We don't want in_nmi() to return true,
-                * but we need to notify RCU.
-                */
-               rcu_nmi_enter();
-       }
-
-       preempt_disable();
-
-       /* This code is a bit fragile.  Test it. */
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "ist_enter didn't work");
-}
-NOKPROBE_SYMBOL(ist_enter);
-
-void ist_exit(struct pt_regs *regs)
-{
-       preempt_enable_no_resched();
-
-       if (!user_mode(regs))
-               rcu_nmi_exit();
-}
-
-/**
- * ist_begin_non_atomic() - begin a non-atomic section in an IST exception
- * @regs:      regs passed to the IST exception handler
- *
- * IST exception handlers normally cannot schedule.  As a special
- * exception, if the exception interrupted userspace code (i.e.
- * user_mode(regs) would return true) and the exception was not
- * a double fault, it can be safe to schedule.  ist_begin_non_atomic()
- * begins a non-atomic section within an ist_enter()/ist_exit() region.
- * Callers are responsible for enabling interrupts themselves inside
- * the non-atomic section, and callers must call ist_end_non_atomic()
- * before ist_exit().
- */
-void ist_begin_non_atomic(struct pt_regs *regs)
-{
-       BUG_ON(!user_mode(regs));
-
-       /*
-        * Sanity check: we need to be on the normal thread stack.  This
-        * will catch asm bugs and any attempt to use ist_preempt_enable
-        * from double_fault.
-        */
-       BUG_ON(!on_thread_stack());
-
-       preempt_enable_no_resched();
-}
-
-/**
- * ist_end_non_atomic() - begin a non-atomic section in an IST exception
- *
- * Ends a non-atomic section started with ist_begin_non_atomic().
- */
-void ist_end_non_atomic(void)
-{
-       preempt_disable();
-}
-
 int is_valid_bugaddr(unsigned long addr)
 {
        unsigned short ud;
@@ -326,7 +256,6 @@ __visible void __noreturn handle_stack_overflow(const char *message,
 }
 #endif
 
-#if defined(CONFIG_X86_64) || defined(CONFIG_DOUBLEFAULT)
 /*
  * Runs on an IST stack for x86_64 and on a special task stack for x86_32.
  *
@@ -363,7 +292,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
         * The net result is that our #GP handler will think that we
         * entered from usermode with the bad user context.
         *
-        * No need for ist_enter here because we don't use RCU.
+        * No need for nmi_enter() here because we don't use RCU.
         */
        if (((long)regs->sp >> P4D_SHIFT) == ESPFIX_PGD_ENTRY &&
                regs->cs == __KERNEL_CS &&
@@ -398,7 +327,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
        }
 #endif
 
-       ist_enter(regs);
+       nmi_enter();
        notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
 
        tsk->thread.error_code = error_code;
@@ -450,7 +379,6 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
        die("double fault", regs, error_code);
        panic("Machine halted.");
 }
-#endif
 
 dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
 {
@@ -592,19 +520,13 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
                return;
 
        /*
-        * Unlike any other non-IST entry, we can be called from a kprobe in
-        * non-CONTEXT_KERNEL kernel mode or even during context tracking
-        * state changes.  Make sure that we wake up RCU even if we're coming
-        * from kernel code.
-        *
-        * This means that we can't schedule even if we came from a
-        * preemptible kernel context.  That's okay.
+        * Unlike any other non-IST entry, we can be called from pretty much
+        * any location in the kernel through kprobes -- text_poke() will most
+        * likely be handled by poke_int3_handler() above. This means this
+        * handler is effectively NMI-like.
         */
-       if (!user_mode(regs)) {
-               rcu_nmi_enter();
-               preempt_disable();
-       }
-       RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
+       if (!user_mode(regs))
+               nmi_enter();
 
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
        if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
@@ -626,10 +548,8 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
        cond_local_irq_disable(regs);
 
 exit:
-       if (!user_mode(regs)) {
-               preempt_enable_no_resched();
-               rcu_nmi_exit();
-       }
+       if (!user_mode(regs))
+               nmi_exit();
 }
 NOKPROBE_SYMBOL(do_int3);
 
@@ -733,7 +653,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
        unsigned long dr6;
        int si_code;
 
-       ist_enter(regs);
+       nmi_enter();
 
        get_debugreg(dr6, 6);
        /*
@@ -826,7 +746,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
        debug_stack_usage_dec();
 
 exit:
-       ist_exit(regs);
+       nmi_exit();
 }
 NOKPROBE_SYMBOL(do_debug);
 
index 8071952e9cf27a37a913fe1b634e358930fb4ef4..fd59fee846315d10f6208bcd46f14115cf0fcadd 100644 (file)
@@ -3586,7 +3586,7 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
                /*
                 * Currently, fast page fault only works for direct mapping
                 * since the gfn is not stable for indirect shadow page. See
-                * Documentation/virt/kvm/locking.txt to get more detail.
+                * Documentation/virt/kvm/locking.rst to get more detail.
                 */
                fault_handled = fast_pf_fix_direct_spte(vcpu, sp,
                                                        iterator.sptep, spte,
index 89f7f3aebd31b102eee3e20443455e850fcddef2..5573a97f1520110c77beea38dae241c7edd64046 100644 (file)
@@ -336,8 +336,7 @@ static struct page **sev_pin_memory(struct kvm *kvm, unsigned long uaddr,
        /* Avoid using vmalloc for smaller buffers. */
        size = npages * sizeof(struct page *);
        if (size > PAGE_SIZE)
-               pages = __vmalloc(size, GFP_KERNEL_ACCOUNT | __GFP_ZERO,
-                                 PAGE_KERNEL);
+               pages = __vmalloc(size, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
        else
                pages = kmalloc(size, GFP_KERNEL_ACCOUNT);
 
index 4742e8fa7ee79b3db45310f8931c668902beefa3..d1d76891236804eaa073c3078255fcbbe7d7a4c1 100644 (file)
@@ -153,7 +153,7 @@ SYM_FUNC_START(csum_partial)
        negl %ebx
        lea 45f(%ebx,%ebx,2), %ebx
        testl %esi, %esi
-       JMP_NOSPEC %ebx
+       JMP_NOSPEC ebx
 
        # Handle 2-byte-aligned regions
 20:    addw (%esi), %ax
@@ -436,7 +436,7 @@ SYM_FUNC_START(csum_partial_copy_generic)
        andl $-32,%edx
        lea 3f(%ebx,%ebx), %ebx
        testl %esi, %esi 
-       JMP_NOSPEC %ebx
+       JMP_NOSPEC ebx
 1:     addl $64,%esi
        addl $64,%edi 
        SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
index c66c8b00f236c1a0fe850a4aa75401aeaedd83d8..ee63d7576fd2c17094dfb66b142b2f7778ec2495 100644 (file)
@@ -10,7 +10,7 @@
 #include <asm/smap.h>
 
 /**
- * csum_partial_copy_from_user - Copy and checksum from user space.
+ * csum_and_copy_from_user - Copy and checksum from user space.
  * @src: source address (user space)
  * @dst: destination address
  * @len: number of bytes to be copied.
  * src and dst are best aligned to 64bits.
  */
 __wsum
-csum_partial_copy_from_user(const void __user *src, void *dst,
+csum_and_copy_from_user(const void __user *src, void *dst,
                            int len, __wsum isum, int *errp)
 {
        might_sleep();
        *errp = 0;
 
-       if (!likely(access_ok(src, len)))
+       if (!user_access_begin(src, len))
                goto out_err;
 
        /*
@@ -42,8 +42,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
                while (((unsigned long)src & 6) && len >= 2) {
                        __u16 val16;
 
-                       if (__get_user(val16, (const __u16 __user *)src))
-                               goto out_err;
+                       unsafe_get_user(val16, (const __u16 __user *)src, out);
 
                        *(__u16 *)dst = val16;
                        isum = (__force __wsum)add32_with_carry(
@@ -53,25 +52,26 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
                        len -= 2;
                }
        }
-       stac();
        isum = csum_partial_copy_generic((__force const void *)src,
                                dst, len, isum, errp, NULL);
-       clac();
+       user_access_end();
        if (unlikely(*errp))
                goto out_err;
 
        return isum;
 
+out:
+       user_access_end();
 out_err:
        *errp = -EFAULT;
        memset(dst, 0, len);
 
        return isum;
 }
-EXPORT_SYMBOL(csum_partial_copy_from_user);
+EXPORT_SYMBOL(csum_and_copy_from_user);
 
 /**
- * csum_partial_copy_to_user - Copy and checksum to user space.
+ * csum_and_copy_to_user - Copy and checksum to user space.
  * @src: source address
  * @dst: destination address (user space)
  * @len: number of bytes to be copied.
@@ -82,14 +82,14 @@ EXPORT_SYMBOL(csum_partial_copy_from_user);
  * src and dst are best aligned to 64bits.
  */
 __wsum
-csum_partial_copy_to_user(const void *src, void __user *dst,
+csum_and_copy_to_user(const void *src, void __user *dst,
                          int len, __wsum isum, int *errp)
 {
        __wsum ret;
 
        might_sleep();
 
-       if (unlikely(!access_ok(dst, len))) {
+       if (!user_access_begin(dst, len)) {
                *errp = -EFAULT;
                return 0;
        }
@@ -100,9 +100,7 @@ csum_partial_copy_to_user(const void *src, void __user *dst,
 
                        isum = (__force __wsum)add32_with_carry(
                                        (__force unsigned)isum, val16);
-                       *errp = __put_user(val16, (__u16 __user *)dst);
-                       if (*errp)
-                               return isum;
+                       unsafe_put_user(val16, (__u16 __user *)dst, out);
                        src += 2;
                        dst += 2;
                        len -= 2;
@@ -110,13 +108,16 @@ csum_partial_copy_to_user(const void *src, void __user *dst,
        }
 
        *errp = 0;
-       stac();
        ret = csum_partial_copy_generic(src, (void __force *)dst,
                                        len, isum, NULL, errp);
-       clac();
+       user_access_end();
        return ret;
+out:
+       user_access_end();
+       *errp = -EFAULT;
+       return isum;
 }
-EXPORT_SYMBOL(csum_partial_copy_to_user);
+EXPORT_SYMBOL(csum_and_copy_to_user);
 
 /**
  * csum_partial_copy_nocheck - Copy and checksum.
index 363ec132df7eefe054709444b27a477b75189bfa..b4c43a9b148362052a9c94f76dac3362ef20e225 100644 (file)
@@ -7,15 +7,31 @@
 #include <asm/alternative-asm.h>
 #include <asm/export.h>
 #include <asm/nospec-branch.h>
+#include <asm/unwind_hints.h>
+#include <asm/frame.h>
 
 .macro THUNK reg
        .section .text.__x86.indirect_thunk
 
+       .align 32
 SYM_FUNC_START(__x86_indirect_thunk_\reg)
-       CFI_STARTPROC
-       JMP_NOSPEC %\reg
-       CFI_ENDPROC
+       JMP_NOSPEC \reg
 SYM_FUNC_END(__x86_indirect_thunk_\reg)
+
+SYM_FUNC_START_NOALIGN(__x86_retpoline_\reg)
+       ANNOTATE_INTRA_FUNCTION_CALL
+       call    .Ldo_rop_\@
+.Lspec_trap_\@:
+       UNWIND_HINT_EMPTY
+       pause
+       lfence
+       jmp     .Lspec_trap_\@
+.Ldo_rop_\@:
+       mov     %\reg, (%_ASM_SP)
+       UNWIND_HINT_RET_OFFSET
+       ret
+SYM_FUNC_END(__x86_retpoline_\reg)
+
 .endm
 
 /*
@@ -24,25 +40,24 @@ SYM_FUNC_END(__x86_indirect_thunk_\reg)
  * only see one instance of "__x86_indirect_thunk_\reg" rather
  * than one per register with the correct names. So we do it
  * the simple and nasty way...
+ *
+ * Worse, you can only have a single EXPORT_SYMBOL per line,
+ * and CPP can't insert newlines, so we have to repeat everything
+ * at least twice.
  */
-#define __EXPORT_THUNK(sym) _ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
-#define EXPORT_THUNK(reg) __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
-#define GENERATE_THUNK(reg) THUNK reg ; EXPORT_THUNK(reg)
-
-GENERATE_THUNK(_ASM_AX)
-GENERATE_THUNK(_ASM_BX)
-GENERATE_THUNK(_ASM_CX)
-GENERATE_THUNK(_ASM_DX)
-GENERATE_THUNK(_ASM_SI)
-GENERATE_THUNK(_ASM_DI)
-GENERATE_THUNK(_ASM_BP)
-#ifdef CONFIG_64BIT
-GENERATE_THUNK(r8)
-GENERATE_THUNK(r9)
-GENERATE_THUNK(r10)
-GENERATE_THUNK(r11)
-GENERATE_THUNK(r12)
-GENERATE_THUNK(r13)
-GENERATE_THUNK(r14)
-GENERATE_THUNK(r15)
-#endif
+
+#define __EXPORT_THUNK(sym)    _ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
+#define EXPORT_THUNK(reg)      __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
+#define EXPORT_RETPOLINE(reg)  __EXPORT_THUNK(__x86_retpoline_ ## reg)
+
+#undef GEN
+#define GEN(reg) THUNK reg
+#include <asm/GEN-for-each-reg.h>
+
+#undef GEN
+#define GEN(reg) EXPORT_THUNK(reg)
+#include <asm/GEN-for-each-reg.h>
+
+#undef GEN
+#define GEN(reg) EXPORT_RETPOLINE(reg)
+#include <asm/GEN-for-each-reg.h>
index 56f9189bbadb4700973d3ff1b1be85c710ffd463..5199d8a1daf1f0ce3a82c6e301eba7efad25737c 100644 (file)
@@ -17,7 +17,7 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks);
 DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks);
 #endif
 
-#if defined(CONFIG_X86_32) && defined(CONFIG_DOUBLEFAULT)
+#ifdef CONFIG_X86_32
 DECLARE_PER_CPU_PAGE_ALIGNED(struct doublefault_stack, doublefault_stack);
 #endif
 
@@ -114,12 +114,10 @@ static void __init percpu_setup_exception_stacks(unsigned int cpu)
 #else
 static inline void percpu_setup_exception_stacks(unsigned int cpu)
 {
-#ifdef CONFIG_DOUBLEFAULT
        struct cpu_entry_area *cea = get_cpu_entry_area(cpu);
 
        cea_map_percpu_pages(&cea->doublefault_stack,
                             &per_cpu(doublefault_stack, cpu), 1, PAGE_KERNEL);
-#endif
 }
 #endif
 
index 69309cd56fdf3fb28c8b7732297471ba69f46686..ea9010113f69d921ed5e8428d5cefb06c06623db 100644 (file)
@@ -249,10 +249,22 @@ static void note_wx(struct pg_state *st, unsigned long addr)
                  (void *)st->start_address);
 }
 
-static inline pgprotval_t effective_prot(pgprotval_t prot1, pgprotval_t prot2)
+static void effective_prot(struct ptdump_state *pt_st, int level, u64 val)
 {
-       return (prot1 & prot2 & (_PAGE_USER | _PAGE_RW)) |
-              ((prot1 | prot2) & _PAGE_NX);
+       struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
+       pgprotval_t prot = val & PTE_FLAGS_MASK;
+       pgprotval_t effective;
+
+       if (level > 0) {
+               pgprotval_t higher_prot = st->prot_levels[level - 1];
+
+               effective = (higher_prot & prot & (_PAGE_USER | _PAGE_RW)) |
+                           ((higher_prot | prot) & _PAGE_NX);
+       } else {
+               effective = prot;
+       }
+
+       st->prot_levels[level] = effective;
 }
 
 /*
@@ -261,7 +273,7 @@ static inline pgprotval_t effective_prot(pgprotval_t prot1, pgprotval_t prot2)
  * print what we collected so far.
  */
 static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
-                     unsigned long val)
+                     u64 val)
 {
        struct pg_state *st = container_of(pt_st, struct pg_state, ptdump);
        pgprotval_t new_prot, new_eff;
@@ -270,16 +282,10 @@ static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
        struct seq_file *m = st->seq;
 
        new_prot = val & PTE_FLAGS_MASK;
-
-       if (level > 0) {
-               new_eff = effective_prot(st->prot_levels[level - 1],
-                                        new_prot);
-       } else {
-               new_eff = new_prot;
-       }
-
-       if (level >= 0)
-               st->prot_levels[level] = new_eff;
+       if (!val)
+               new_eff = 0;
+       else
+               new_eff = st->prot_levels[level];
 
        /*
         * If we have a "break" in the series, we need to flush the state that
@@ -374,6 +380,7 @@ static void ptdump_walk_pgd_level_core(struct seq_file *m,
        struct pg_state st = {
                .ptdump = {
                        .note_page      = note_page,
+                       .effective_prot = effective_prot,
                        .range          = ptdump_ranges
                },
                .level = -1,
index a51df516b87bf1e174e2170b2c642406d2f28633..dffe8e4d3140efa283c30d75d7eb3552d8ca800c 100644 (file)
@@ -190,16 +190,13 @@ static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
        return pmd_k;
 }
 
-static void vmalloc_sync(void)
+void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
 {
-       unsigned long address;
-
-       if (SHARED_KERNEL_PMD)
-               return;
+       unsigned long addr;
 
-       for (address = VMALLOC_START & PMD_MASK;
-            address >= TASK_SIZE_MAX && address < VMALLOC_END;
-            address += PMD_SIZE) {
+       for (addr = start & PMD_MASK;
+            addr >= TASK_SIZE_MAX && addr < VMALLOC_END;
+            addr += PMD_SIZE) {
                struct page *page;
 
                spin_lock(&pgd_lock);
@@ -210,61 +207,13 @@ static void vmalloc_sync(void)
                        pgt_lock = &pgd_page_get_mm(page)->page_table_lock;
 
                        spin_lock(pgt_lock);
-                       vmalloc_sync_one(page_address(page), address);
+                       vmalloc_sync_one(page_address(page), addr);
                        spin_unlock(pgt_lock);
                }
                spin_unlock(&pgd_lock);
        }
 }
 
-void vmalloc_sync_mappings(void)
-{
-       vmalloc_sync();
-}
-
-void vmalloc_sync_unmappings(void)
-{
-       vmalloc_sync();
-}
-
-/*
- * 32-bit:
- *
- *   Handle a fault on the vmalloc or module mapping area
- */
-static noinline int vmalloc_fault(unsigned long address)
-{
-       unsigned long pgd_paddr;
-       pmd_t *pmd_k;
-       pte_t *pte_k;
-
-       /* Make sure we are in vmalloc area: */
-       if (!(address >= VMALLOC_START && address < VMALLOC_END))
-               return -1;
-
-       /*
-        * Synchronize this task's top level page-table
-        * with the 'reference' page table.
-        *
-        * Do _not_ use "current" here. We might be inside
-        * an interrupt in the middle of a task switch..
-        */
-       pgd_paddr = read_cr3_pa();
-       pmd_k = vmalloc_sync_one(__va(pgd_paddr), address);
-       if (!pmd_k)
-               return -1;
-
-       if (pmd_large(*pmd_k))
-               return 0;
-
-       pte_k = pte_offset_kernel(pmd_k, address);
-       if (!pte_present(*pte_k))
-               return -1;
-
-       return 0;
-}
-NOKPROBE_SYMBOL(vmalloc_fault);
-
 /*
  * Did it hit the DOS screen memory VA from vm86 mode?
  */
@@ -329,96 +278,6 @@ out:
 
 #else /* CONFIG_X86_64: */
 
-void vmalloc_sync_mappings(void)
-{
-       /*
-        * 64-bit mappings might allocate new p4d/pud pages
-        * that need to be propagated to all tasks' PGDs.
-        */
-       sync_global_pgds(VMALLOC_START & PGDIR_MASK, VMALLOC_END);
-}
-
-void vmalloc_sync_unmappings(void)
-{
-       /*
-        * Unmappings never allocate or free p4d/pud pages.
-        * No work is required here.
-        */
-}
-
-/*
- * 64-bit:
- *
- *   Handle a fault on the vmalloc area
- */
-static noinline int vmalloc_fault(unsigned long address)
-{
-       pgd_t *pgd, *pgd_k;
-       p4d_t *p4d, *p4d_k;
-       pud_t *pud;
-       pmd_t *pmd;
-       pte_t *pte;
-
-       /* Make sure we are in vmalloc area: */
-       if (!(address >= VMALLOC_START && address < VMALLOC_END))
-               return -1;
-
-       /*
-        * Copy kernel mappings over when needed. This can also
-        * happen within a race in page table update. In the later
-        * case just flush:
-        */
-       pgd = (pgd_t *)__va(read_cr3_pa()) + pgd_index(address);
-       pgd_k = pgd_offset_k(address);
-       if (pgd_none(*pgd_k))
-               return -1;
-
-       if (pgtable_l5_enabled()) {
-               if (pgd_none(*pgd)) {
-                       set_pgd(pgd, *pgd_k);
-                       arch_flush_lazy_mmu_mode();
-               } else {
-                       BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_k));
-               }
-       }
-
-       /* With 4-level paging, copying happens on the p4d level. */
-       p4d = p4d_offset(pgd, address);
-       p4d_k = p4d_offset(pgd_k, address);
-       if (p4d_none(*p4d_k))
-               return -1;
-
-       if (p4d_none(*p4d) && !pgtable_l5_enabled()) {
-               set_p4d(p4d, *p4d_k);
-               arch_flush_lazy_mmu_mode();
-       } else {
-               BUG_ON(p4d_pfn(*p4d) != p4d_pfn(*p4d_k));
-       }
-
-       BUILD_BUG_ON(CONFIG_PGTABLE_LEVELS < 4);
-
-       pud = pud_offset(p4d, address);
-       if (pud_none(*pud))
-               return -1;
-
-       if (pud_large(*pud))
-               return 0;
-
-       pmd = pmd_offset(pud, address);
-       if (pmd_none(*pmd))
-               return -1;
-
-       if (pmd_large(*pmd))
-               return 0;
-
-       pte = pte_offset_kernel(pmd, address);
-       if (!pte_present(*pte))
-               return -1;
-
-       return 0;
-}
-NOKPROBE_SYMBOL(vmalloc_fault);
-
 #ifdef CONFIG_CPU_SUP_AMD
 static const char errata93_warning[] =
 KERN_ERR 
@@ -1257,29 +1116,6 @@ do_kern_addr_fault(struct pt_regs *regs, unsigned long hw_error_code,
         */
        WARN_ON_ONCE(hw_error_code & X86_PF_PK);
 
-       /*
-        * We can fault-in kernel-space virtual memory on-demand. The
-        * 'reference' page table is init_mm.pgd.
-        *
-        * NOTE! We MUST NOT take any locks for this case. We may
-        * be in an interrupt or a critical region, and should
-        * only copy the information from the master page table,
-        * nothing more.
-        *
-        * Before doing this on-demand faulting, ensure that the
-        * fault is not any of the following:
-        * 1. A fault on a PTE with a reserved bit set.
-        * 2. A fault caused by a user-mode access.  (Do not demand-
-        *    fault kernel memory due to user-mode accesses).
-        * 3. A fault caused by a page-level protection violation.
-        *    (A demand fault would be on a non-present page which
-        *     would have X86_PF_PROT==0).
-        */
-       if (!(hw_error_code & (X86_PF_RSVD | X86_PF_USER | X86_PF_PROT))) {
-               if (vmalloc_fault(address) >= 0)
-                       return;
-       }
-
        /* Was the fault spurious, caused by lazy TLB invalidation? */
        if (spurious_kernel_fault(hw_error_code, address))
                return;
index 1bba16c5742b3307bfbb69f9978cc489062e92e1..a573a3e63f02c0e7acfc2be613d24f14506df21b 100644 (file)
@@ -121,8 +121,6 @@ __ref void *alloc_low_pages(unsigned int num)
        } else {
                pfn = pgt_buf_end;
                pgt_buf_end += num;
-               printk(KERN_DEBUG "BRK [%#010lx, %#010lx] PGTABLE\n",
-                       pfn << PAGE_SHIFT, (pgt_buf_end << PAGE_SHIFT) - 1);
        }
 
        for (i = 0; i < num; i++) {
index 8b5f73f5e207c3e1a0d18a5cbf9ba0049088a309..96274a90c5ff7ea8ba20a8787a6fb86b1db43a1b 100644 (file)
@@ -218,6 +218,11 @@ void sync_global_pgds(unsigned long start, unsigned long end)
                sync_global_pgds_l4(start, end);
 }
 
+void arch_sync_kernel_mappings(unsigned long start, unsigned long end)
+{
+       sync_global_pgds(start, end);
+}
+
 /*
  * NOTE: This function is marked __ref because it calls __init function
  * (alloc_bootmem_pages). It's safe to do it ONLY when after_bootmem == 0.
index cb91eccc4960cd95b267d9f16ad98b48de6ec1d7..c90c20904a60750b80e54c090fc844816369fcd9 100644 (file)
@@ -18,7 +18,9 @@
 #include <linux/sched/signal.h>
 #include <linux/sched/mm.h>
 #include <linux/compat.h>
+#include <linux/elf-randomize.h>
 #include <asm/elf.h>
+#include <asm/io.h>
 
 #include "physaddr.h"
 
index f2bd3d61e16bb2157ea227d9533fc4845cf1c79e..104544359d69cd20ef1e37449d32a687ee1f3433 100644 (file)
 
 #include "numa_internal.h"
 
-#ifdef CONFIG_DISCONTIGMEM
-/*
- * 4) physnode_map     - the mapping between a pfn and owning node
- * physnode_map keeps track of the physical memory layout of a generic
- * numa node on a 64Mb break (each element of the array will
- * represent 64Mb of memory and will be marked by the node id.  so,
- * if the first gig is on node 0, and the second gig is on node 1
- * physnode_map will contain:
- *
- *     physnode_map[0-15] = 0;
- *     physnode_map[16-31] = 1;
- *     physnode_map[32- ] = -1;
- */
-s8 physnode_map[MAX_SECTIONS] __read_mostly = { [0 ... (MAX_SECTIONS - 1)] = -1};
-EXPORT_SYMBOL(physnode_map);
-
-void memory_present(int nid, unsigned long start, unsigned long end)
-{
-       unsigned long pfn;
-
-       printk(KERN_INFO "Node: %d, start_pfn: %lx, end_pfn: %lx\n",
-                       nid, start, end);
-       printk(KERN_DEBUG "  Setting physnode_map array to node %d for pfns:\n", nid);
-       printk(KERN_DEBUG "  ");
-       start = round_down(start, PAGES_PER_SECTION);
-       end = round_up(end, PAGES_PER_SECTION);
-       for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) {
-               physnode_map[pfn / PAGES_PER_SECTION] = nid;
-               printk(KERN_CONT "%lx ", pfn);
-       }
-       printk(KERN_CONT "\n");
-}
-#endif
-
 extern unsigned long highend_pfn, highstart_pfn;
 
 void __init initmem_init(void)
index 843aa10a4cb6163fb72a33cd80fac5de224034e2..da0fb17a1a36a0cd3b5377c7cab3dcd5aaccb188 100644 (file)
@@ -448,13 +448,7 @@ static void __init pti_clone_user_shared(void)
                 * the sp1 and sp2 slots.
                 *
                 * This is done for all possible CPUs during boot to ensure
-                * that it's propagated to all mms.  If we were to add one of
-                * these mappings during CPU hotplug, we would need to take
-                * some measure to make sure that every mm that subsequently
-                * ran on that CPU would have the relevant PGD entry in its
-                * pagetables.  The usual vmalloc_fault() mechanism would not
-                * work for page faults taken in entry_SYSCALL_64 before RSP
-                * is set up.
+                * that it's propagated to all mms.
                 */
 
                unsigned long va = (unsigned long)&per_cpu(cpu_tss_rw, cpu);
index 66f96f21a7b60b2a73832fd291c1f7ae783cb2c5..f3fe261e59368c3e8ddf1b30d6c52bb6faca5c07 100644 (file)
@@ -161,34 +161,6 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
        local_irq_restore(flags);
 }
 
-static void sync_current_stack_to_mm(struct mm_struct *mm)
-{
-       unsigned long sp = current_stack_pointer;
-       pgd_t *pgd = pgd_offset(mm, sp);
-
-       if (pgtable_l5_enabled()) {
-               if (unlikely(pgd_none(*pgd))) {
-                       pgd_t *pgd_ref = pgd_offset_k(sp);
-
-                       set_pgd(pgd, *pgd_ref);
-               }
-       } else {
-               /*
-                * "pgd" is faked.  The top level entries are "p4d"s, so sync
-                * the p4d.  This compiles to approximately the same code as
-                * the 5-level case.
-                */
-               p4d_t *p4d = p4d_offset(pgd, sp);
-
-               if (unlikely(p4d_none(*p4d))) {
-                       pgd_t *pgd_ref = pgd_offset_k(sp);
-                       p4d_t *p4d_ref = p4d_offset(pgd_ref, sp);
-
-                       set_p4d(p4d, *p4d_ref);
-               }
-       }
-}
-
 static inline unsigned long mm_mangle_tif_spec_ib(struct task_struct *next)
 {
        unsigned long next_tif = task_thread_info(next)->flags;
@@ -377,15 +349,6 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                 */
                cond_ibpb(tsk);
 
-               if (IS_ENABLED(CONFIG_VMAP_STACK)) {
-                       /*
-                        * If our current stack is in vmalloc space and isn't
-                        * mapped in the new pgd, we'll double-fault.  Forcibly
-                        * map it.
-                        */
-                       sync_current_stack_to_mm(next);
-               }
-
                /*
                 * Stop remote flushes for the previous mm.
                 * Skip kernel threads; we never send init_mm TLB flushing IPIs,
index 1aae5302501d0d69f66abc11b7a10811dd6ee0f1..e966115d105c412e554f2e64d8979549c86b079e 100644 (file)
@@ -62,12 +62,12 @@ static unsigned long efi_runtime, efi_nr_tables;
 unsigned long efi_fw_vendor, efi_config_table;
 
 static const efi_config_table_type_t arch_tables[] __initconst = {
-       {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys},
-       {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys},
+       {EFI_PROPERTIES_TABLE_GUID,     &prop_phys,             "PROP"          },
+       {UGA_IO_PROTOCOL_GUID,          &uga_phys,              "UGA"           },
 #ifdef CONFIG_X86_UV
-       {UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys},
+       {UV_SYSTEM_TABLE_GUID,          &uv_systab_phys,        "UVsystab"      },
 #endif
-       {NULL_GUID, NULL, NULL},
+       {},
 };
 
 static const unsigned long * const efi_tables[] = {
index 15da118f04f04aec93b1c561b97fc326e279958b..90380a17ab2386ec5ea377cd2a1487b37cdd99d0 100644 (file)
@@ -21,7 +21,7 @@ SYM_FUNC_START(__efi_call)
        mov %r8, %r9
        mov %rcx, %r8
        mov %rsi, %rcx
-       CALL_NOSPEC %rdi
+       CALL_NOSPEC rdi
        leave
        ret
 SYM_FUNC_END(__efi_call)
index c60255da5a6cd00641177a05051a5877c4ca867f..4494589a288ae60506bbb5ed9f911075183e6117 100644 (file)
@@ -45,7 +45,8 @@ static s64 __uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3,
        return ret;
 }
 
-s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4, u64 a5)
+static s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4,
+               u64 a5)
 {
        s64 ret;
 
@@ -57,10 +58,9 @@ s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4, u64 a5)
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(uv_bios_call);
 
-s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3,
-                                       u64 a4, u64 a5)
+static s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3,
+               u64 a4, u64 a5)
 {
        unsigned long bios_flags;
        s64 ret;
@@ -77,18 +77,13 @@ s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3,
        return ret;
 }
 
-
 long sn_partition_id;
 EXPORT_SYMBOL_GPL(sn_partition_id);
 long sn_coherency_id;
-EXPORT_SYMBOL_GPL(sn_coherency_id);
 long sn_region_size;
 EXPORT_SYMBOL_GPL(sn_region_size);
 long system_serial_number;
-EXPORT_SYMBOL_GPL(system_serial_number);
 int uv_type;
-EXPORT_SYMBOL_GPL(uv_type);
-
 
 s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher,
                long *region, long *ssn)
@@ -115,7 +110,6 @@ s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher,
                *ssn = v1;
        return ret;
 }
-EXPORT_SYMBOL_GPL(uv_bios_get_sn_info);
 
 int
 uv_bios_mq_watchlist_alloc(unsigned long addr, unsigned int mq_size,
@@ -166,7 +160,6 @@ s64 uv_bios_freq_base(u64 clock_type, u64 *ticks_per_second)
        return uv_bios_call(UV_BIOS_FREQ_BASE, clock_type,
                           (u64)ticks_per_second, 0, 0, 0);
 }
-EXPORT_SYMBOL_GPL(uv_bios_freq_base);
 
 /*
  * uv_bios_set_legacy_vga_target - Set Legacy VGA I/O Target
@@ -185,7 +178,6 @@ int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus)
        return uv_bios_call(UV_BIOS_SET_LEGACY_VGA_TARGET,
                                (u64)decode, (u64)domain, (u64)bus, 0, 0);
 }
-EXPORT_SYMBOL_GPL(uv_bios_set_legacy_vga_target);
 
 int uv_bios_init(void)
 {
index 62214731fea5e54bca7b7c99f494fcac026dcc68..266773e2fb379ea92241a8dd0d3e26a07a7987d3 100644 (file)
@@ -21,7 +21,7 @@ static ssize_t partition_id_show(struct kobject *kobj,
 static ssize_t coherence_id_show(struct kobject *kobj,
                        struct kobj_attribute *attr, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%ld\n", uv_partition_coherence_id());
+       return snprintf(buf, PAGE_SIZE, "%ld\n", sn_coherency_id);
 }
 
 static struct kobj_attribute partition_id_attr =
index aaff9ed7ff45c35f818fc2d355713033a8f4600e..fc3b757afb2c9a31ef98b255bf14a0de26ee6c12 100644 (file)
@@ -307,7 +307,7 @@ int hibernate_resume_nonboot_cpu_disable(void)
        if (ret)
                return ret;
        smp_ops.play_dead = resume_play_dead;
-       ret = disable_nonboot_cpus();
+       ret = freeze_secondary_cpus(0);
        smp_ops.play_dead = play_dead;
        return ret;
 }
index 2a56cac646879d219885ea446e03aa9362c8782b..ff6bba2c8ab61d9b85608327c599bd29d3e0216f 100644 (file)
@@ -36,26 +36,6 @@ __wsum csum_partial_copy_nocheck(const void *src, void *dst,
        return csum_partial(dst, len, sum);
 }
 
-/*
- * the same as csum_partial, but copies from src while it
- * checksums, and handles user-space pointer exceptions correctly, when needed.
- *
- * here even more important to align src and dst on a 32-bit (or even
- * better 64-bit) boundary
- */
-
-static __inline__
-__wsum csum_partial_copy_from_user(const void __user *src, void *dst,
-                                        int len, __wsum sum, int *err_ptr)
-{
-       if (copy_from_user(dst, src, len)) {
-               *err_ptr = -EFAULT;
-               return (__force __wsum)-1;
-       }
-
-       return csum_partial(dst, len, sum);
-}
-
 /**
  * csum_fold - Fold and invert a 32bit checksum.
  * sum: 32bit unfolded sum
index 1abe455d926a2785eb8049576f9ad345fbff2d8e..205a9bc981b0c1c261b0aa39be2f6650c4d99951 100644 (file)
@@ -29,7 +29,7 @@ static efi_system_table_t efi_systab_xen __initdata = {
        .fw_vendor      = EFI_INVALID_TABLE_ADDR, /* Initialized later. */
        .fw_revision    = 0,                      /* Initialized later. */
        .con_in_handle  = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
-       .con_in         = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
+       .con_in         = NULL,                   /* Not used under Xen. */
        .con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
        .con_out        = NULL,                   /* Not used under Xen. */
        .stderr_handle  = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */
index 8b687176ad7245cfef1e9b06dd0344a0b72eafb0..d8292cc9ebdf88c3389d0ed9a50a2eea1be25790 100644 (file)
@@ -44,8 +44,6 @@ asmlinkage __wsum csum_partial_copy_generic(const void *src, void *dst,
 /*
  *     Note: when you get a NULL pointer exception here this means someone
  *     passed in an incorrect kernel address to one of these functions.
- *
- *     If you use these functions directly please don't forget the access_ok().
  */
 static inline
 __wsum csum_partial_copy_nocheck(const void *src, void *dst,
@@ -54,12 +52,17 @@ __wsum csum_partial_copy_nocheck(const void *src, void *dst,
        return csum_partial_copy_generic(src, dst, len, sum, NULL, NULL);
 }
 
+#define _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
 static inline
-__wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+__wsum csum_and_copy_from_user(const void __user *src, void *dst,
                                   int len, __wsum sum, int *err_ptr)
 {
-       return csum_partial_copy_generic((__force const void *)src, dst,
+       if (access_ok(dst, len))
+               return csum_partial_copy_generic((__force const void *)src, dst,
                                        len, sum, err_ptr, NULL);
+       if (len)
+               *err_ptr = -EFAULT;
+       return sum;
 }
 
 /*
index 85a9ab1bc04dab5cae943dbe168aedfe452fa6de..69d0d73876b31070dd3d3a98a83b411e7dded16e 100644 (file)
 435    common  clone3                          sys_clone3
 437    common  openat2                         sys_openat2
 438    common  pidfd_getfd                     sys_pidfd_getfd
+439    common  faccessat2                      sys_faccessat2
index 7e4a1da0715ea85c4f1340522b97e5ec1f4d9d04..38d7b1f160673405b1ac9c57a459d1dd6f0ceb11 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/blk-mq.h>
 #include <linux/highmem.h>
 #include <linux/mm.h>
+#include <linux/pagemap.h>
 #include <linux/kernel_stat.h>
 #include <linux/string.h>
 #include <linux/init.h>
@@ -891,14 +892,11 @@ generic_make_request_checks(struct bio *bio)
        }
 
        /*
-        * Non-mq queues do not honor REQ_NOWAIT, so complete a bio
-        * with BLK_STS_AGAIN status in order to catch -EAGAIN and
-        * to give a chance to the caller to repeat request gracefully.
+        * For a REQ_NOWAIT based request, return -EOPNOTSUPP
+        * if queue is not a request based queue.
         */
-       if ((bio->bi_opf & REQ_NOWAIT) && !queue_is_mq(q)) {
-               status = BLK_STS_AGAIN;
-               goto end_io;
-       }
+       if ((bio->bi_opf & REQ_NOWAIT) && !queue_is_mq(q))
+               goto not_supported;
 
        if (should_fail_bio(bio))
                goto end_io;
index c24a47406f8f57b7550a98a20654fde836d212d0..d5daf35431e310a5a47e81197ab4d3be40797ba5 100644 (file)
@@ -370,7 +370,6 @@ config CRYPTO_CFB
 config CRYPTO_CTR
        tristate "CTR support"
        select CRYPTO_SKCIPHER
-       select CRYPTO_SEQIV
        select CRYPTO_MANAGER
        help
          CTR: Counter mode
@@ -1820,7 +1819,7 @@ config CRYPTO_DRBG_HASH
 config CRYPTO_DRBG_CTR
        bool "Enable CTR DRBG"
        select CRYPTO_AES
-       depends on CRYPTO_CTR
+       select CRYPTO_CTR
        help
          Enable the CTR DRBG variant as defined in NIST SP800-90A.
 
index 69605e21af92278181fb879d3c2d6fdd5d90f771..92abdf675992c681ce5206a8c5783fc88495caa8 100644 (file)
@@ -403,7 +403,7 @@ static void crypto_wait_for_test(struct crypto_larval *larval)
        err = wait_for_completion_killable(&larval->completion);
        WARN_ON(err);
        if (!err)
-               crypto_probing_notify(CRYPTO_MSG_ALG_LOADED, larval);
+               crypto_notify(CRYPTO_MSG_ALG_LOADED, larval);
 
 out:
        crypto_larval_kill(&larval->alg);
@@ -716,17 +716,27 @@ EXPORT_SYMBOL_GPL(crypto_drop_spawn);
 
 static struct crypto_alg *crypto_spawn_alg(struct crypto_spawn *spawn)
 {
-       struct crypto_alg *alg;
+       struct crypto_alg *alg = ERR_PTR(-EAGAIN);
+       struct crypto_alg *target;
+       bool shoot = false;
 
        down_read(&crypto_alg_sem);
-       alg = spawn->alg;
-       if (!spawn->dead && !crypto_mod_get(alg)) {
-               alg->cra_flags |= CRYPTO_ALG_DYING;
-               alg = NULL;
+       if (!spawn->dead) {
+               alg = spawn->alg;
+               if (!crypto_mod_get(alg)) {
+                       target = crypto_alg_get(alg);
+                       shoot = true;
+                       alg = ERR_PTR(-EAGAIN);
+               }
        }
        up_read(&crypto_alg_sem);
 
-       return alg ?: ERR_PTR(-EAGAIN);
+       if (shoot) {
+               crypto_shoot_alg(target);
+               crypto_alg_put(target);
+       }
+
+       return alg;
 }
 
 struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type,
@@ -904,6 +914,14 @@ out:
 }
 EXPORT_SYMBOL_GPL(crypto_enqueue_request);
 
+void crypto_enqueue_request_head(struct crypto_queue *queue,
+                                struct crypto_async_request *request)
+{
+       queue->qlen++;
+       list_add(&request->list, &queue->list);
+}
+EXPORT_SYMBOL_GPL(crypto_enqueue_request_head);
+
 struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue)
 {
        struct list_head *request;
index 22df3799a17bb0ba59b88283906cfd5df183735f..087c0ad09d382bdba07791610f0a81b172f1c1d3 100644 (file)
@@ -61,7 +61,7 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
        struct sock *sk = sock->sk;
        struct alg_sock *ask = alg_sk(sk);
        struct rng_ctx *ctx = ask->private;
-       int err = -EFAULT;
+       int err;
        int genlen = 0;
        u8 result[MAXSIZE];
 
index 7d71a9b10e5fdade6aa6389cc38f84771953ff07..edcf690800d4258011c26ae100fdd83c4feae5dc 100644 (file)
@@ -333,12 +333,13 @@ static unsigned int crypto_ctxsize(struct crypto_alg *alg, u32 type, u32 mask)
        return len;
 }
 
-static void crypto_shoot_alg(struct crypto_alg *alg)
+void crypto_shoot_alg(struct crypto_alg *alg)
 {
        down_write(&crypto_alg_sem);
        alg->cra_flags |= CRYPTO_ALG_DYING;
        up_write(&crypto_alg_sem);
 }
+EXPORT_SYMBOL_GPL(crypto_shoot_alg);
 
 struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
                                      u32 mask)
index 1d262374fa4e3c4322782c8119b3e32859f521a7..0ffd8d92e308da966c8ab5e0c4473c8d2c0ff87e 100644 (file)
@@ -129,7 +129,9 @@ static void blake2b_compress(struct blake2b_state *S,
        ROUND(9);
        ROUND(10);
        ROUND(11);
-
+#ifdef CONFIG_CC_IS_CLANG
+#pragma nounroll /* https://bugs.llvm.org/show_bug.cgi?id=45803 */
+#endif
        for (i = 0; i < 8; ++i)
                S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
 }
index eb029ff1e05aa2fd202e8eb816173ab8eca81a72..3655d9d3f5dfb77c47841196d0402f0ca89f5706 100644 (file)
  * @err: error number
  */
 static void crypto_finalize_request(struct crypto_engine *engine,
-                            struct crypto_async_request *req, int err)
+                                   struct crypto_async_request *req, int err)
 {
        unsigned long flags;
-       bool finalize_cur_req = false;
+       bool finalize_req = false;
        int ret;
        struct crypto_engine_ctx *enginectx;
 
-       spin_lock_irqsave(&engine->queue_lock, flags);
-       if (engine->cur_req == req)
-               finalize_cur_req = true;
-       spin_unlock_irqrestore(&engine->queue_lock, flags);
+       /*
+        * If hardware cannot enqueue more requests
+        * and retry mechanism is not supported
+        * make sure we are completing the current request
+        */
+       if (!engine->retry_support) {
+               spin_lock_irqsave(&engine->queue_lock, flags);
+               if (engine->cur_req == req) {
+                       finalize_req = true;
+                       engine->cur_req = NULL;
+               }
+               spin_unlock_irqrestore(&engine->queue_lock, flags);
+       }
 
-       if (finalize_cur_req) {
+       if (finalize_req || engine->retry_support) {
                enginectx = crypto_tfm_ctx(req->tfm);
-               if (engine->cur_req_prepared &&
+               if (enginectx->op.prepare_request &&
                    enginectx->op.unprepare_request) {
                        ret = enginectx->op.unprepare_request(engine, req);
                        if (ret)
                                dev_err(engine->dev, "failed to unprepare request\n");
                }
-               spin_lock_irqsave(&engine->queue_lock, flags);
-               engine->cur_req = NULL;
-               engine->cur_req_prepared = false;
-               spin_unlock_irqrestore(&engine->queue_lock, flags);
        }
-
        req->complete(req, err);
 
        kthread_queue_work(engine->kworker, &engine->pump_requests);
@@ -74,7 +78,7 @@ static void crypto_pump_requests(struct crypto_engine *engine,
        spin_lock_irqsave(&engine->queue_lock, flags);
 
        /* Make sure we are not already running a request */
-       if (engine->cur_req)
+       if (!engine->retry_support && engine->cur_req)
                goto out;
 
        /* If another context is idling then defer */
@@ -108,13 +112,21 @@ static void crypto_pump_requests(struct crypto_engine *engine,
                goto out;
        }
 
+start_request:
        /* Get the fist request from the engine queue to handle */
        backlog = crypto_get_backlog(&engine->queue);
        async_req = crypto_dequeue_request(&engine->queue);
        if (!async_req)
                goto out;
 
-       engine->cur_req = async_req;
+       /*
+        * If hardware doesn't support the retry mechanism,
+        * keep track of the request we are processing now.
+        * We'll need it on completion (crypto_finalize_request).
+        */
+       if (!engine->retry_support)
+               engine->cur_req = async_req;
+
        if (backlog)
                backlog->complete(backlog, -EINPROGRESS);
 
@@ -130,7 +142,7 @@ static void crypto_pump_requests(struct crypto_engine *engine,
                ret = engine->prepare_crypt_hardware(engine);
                if (ret) {
                        dev_err(engine->dev, "failed to prepare crypt hardware\n");
-                       goto req_err;
+                       goto req_err_2;
                }
        }
 
@@ -141,28 +153,90 @@ static void crypto_pump_requests(struct crypto_engine *engine,
                if (ret) {
                        dev_err(engine->dev, "failed to prepare request: %d\n",
                                ret);
-                       goto req_err;
+                       goto req_err_2;
                }
-               engine->cur_req_prepared = true;
        }
        if (!enginectx->op.do_one_request) {
                dev_err(engine->dev, "failed to do request\n");
                ret = -EINVAL;
-               goto req_err;
+               goto req_err_1;
        }
+
        ret = enginectx->op.do_one_request(engine, async_req);
-       if (ret) {
-               dev_err(engine->dev, "Failed to do one request from queue: %d\n", ret);
-               goto req_err;
+
+       /* Request unsuccessfully executed by hardware */
+       if (ret < 0) {
+               /*
+                * If hardware queue is full (-ENOSPC), requeue request
+                * regardless of backlog flag.
+                * Otherwise, unprepare and complete the request.
+                */
+               if (!engine->retry_support ||
+                   (ret != -ENOSPC)) {
+                       dev_err(engine->dev,
+                               "Failed to do one request from queue: %d\n",
+                               ret);
+                       goto req_err_1;
+               }
+               /*
+                * If retry mechanism is supported,
+                * unprepare current request and
+                * enqueue it back into crypto-engine queue.
+                */
+               if (enginectx->op.unprepare_request) {
+                       ret = enginectx->op.unprepare_request(engine,
+                                                             async_req);
+                       if (ret)
+                               dev_err(engine->dev,
+                                       "failed to unprepare request\n");
+               }
+               spin_lock_irqsave(&engine->queue_lock, flags);
+               /*
+                * If hardware was unable to execute request, enqueue it
+                * back in front of crypto-engine queue, to keep the order
+                * of requests.
+                */
+               crypto_enqueue_request_head(&engine->queue, async_req);
+
+               kthread_queue_work(engine->kworker, &engine->pump_requests);
+               goto out;
        }
-       return;
 
-req_err:
-       crypto_finalize_request(engine, async_req, ret);
+       goto retry;
+
+req_err_1:
+       if (enginectx->op.unprepare_request) {
+               ret = enginectx->op.unprepare_request(engine, async_req);
+               if (ret)
+                       dev_err(engine->dev, "failed to unprepare request\n");
+       }
+
+req_err_2:
+       async_req->complete(async_req, ret);
+
+retry:
+       /* If retry mechanism is supported, send new requests to engine */
+       if (engine->retry_support) {
+               spin_lock_irqsave(&engine->queue_lock, flags);
+               goto start_request;
+       }
        return;
 
 out:
        spin_unlock_irqrestore(&engine->queue_lock, flags);
+
+       /*
+        * Batch requests is possible only if
+        * hardware can enqueue multiple requests
+        */
+       if (engine->do_batch_requests) {
+               ret = engine->do_batch_requests(engine);
+               if (ret)
+                       dev_err(engine->dev, "failed to do batch requests: %d\n",
+                               ret);
+       }
+
+       return;
 }
 
 static void crypto_pump_work(struct kthread_work *work)
@@ -386,15 +460,27 @@ int crypto_engine_stop(struct crypto_engine *engine)
 EXPORT_SYMBOL_GPL(crypto_engine_stop);
 
 /**
- * crypto_engine_alloc_init - allocate crypto hardware engine structure and
- * initialize it.
+ * crypto_engine_alloc_init_and_set - allocate crypto hardware engine structure
+ * and initialize it by setting the maximum number of entries in the software
+ * crypto-engine queue.
  * @dev: the device attached with one hardware engine
+ * @retry_support: whether hardware has support for retry mechanism
+ * @cbk_do_batch: pointer to a callback function to be invoked when executing a
+ *                a batch of requests.
+ *                This has the form:
+ *                callback(struct crypto_engine *engine)
+ *                where:
+ *                @engine: the crypto engine structure.
  * @rt: whether this queue is set to run as a realtime task
+ * @qlen: maximum size of the crypto-engine queue
  *
  * This must be called from context that can sleep.
  * Return: the crypto engine structure on success, else NULL.
  */
-struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
+struct crypto_engine *crypto_engine_alloc_init_and_set(struct device *dev,
+                                                      bool retry_support,
+                                                      int (*cbk_do_batch)(struct crypto_engine *engine),
+                                                      bool rt, int qlen)
 {
        struct sched_param param = { .sched_priority = MAX_RT_PRIO / 2 };
        struct crypto_engine *engine;
@@ -411,12 +497,18 @@ struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
        engine->running = false;
        engine->busy = false;
        engine->idling = false;
-       engine->cur_req_prepared = false;
+       engine->retry_support = retry_support;
        engine->priv_data = dev;
+       /*
+        * Batch requests is possible only if
+        * hardware has support for retry mechanism.
+        */
+       engine->do_batch_requests = retry_support ? cbk_do_batch : NULL;
+
        snprintf(engine->name, sizeof(engine->name),
                 "%s-engine", dev_name(dev));
 
-       crypto_init_queue(&engine->queue, CRYPTO_ENGINE_MAX_QLEN);
+       crypto_init_queue(&engine->queue, qlen);
        spin_lock_init(&engine->queue_lock);
 
        engine->kworker = kthread_create_worker(0, "%s", engine->name);
@@ -433,6 +525,22 @@ struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
 
        return engine;
 }
+EXPORT_SYMBOL_GPL(crypto_engine_alloc_init_and_set);
+
+/**
+ * crypto_engine_alloc_init - allocate crypto hardware engine structure and
+ * initialize it.
+ * @dev: the device attached with one hardware engine
+ * @rt: whether this queue is set to run as a realtime task
+ *
+ * This must be called from context that can sleep.
+ * Return: the crypto engine structure on success, else NULL.
+ */
+struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
+{
+       return crypto_engine_alloc_init_and_set(dev, false, NULL, rt,
+                                               CRYPTO_ENGINE_MAX_QLEN);
+}
 EXPORT_SYMBOL_GPL(crypto_engine_alloc_init);
 
 /**
index b6929eb5f565d4b6450f08e3d4d88f1d18cfca77..37526eb8c5d571c7ac10266914a1d89bbb39f5ff 100644 (file)
@@ -1087,10 +1087,6 @@ static void drbg_async_seed(struct work_struct *work)
        if (ret)
                goto unlock;
 
-       /* If nonblocking pool is initialized, deactivate Jitter RNG */
-       crypto_free_rng(drbg->jent);
-       drbg->jent = NULL;
-
        /* Set seeded to false so that if __drbg_seed fails the
         * next generate call will trigger a reseed.
         */
@@ -1168,7 +1164,23 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                                                   entropylen);
                        if (ret) {
                                pr_devel("DRBG: jent failed with %d\n", ret);
-                               goto out;
+
+                               /*
+                                * Do not treat the transient failure of the
+                                * Jitter RNG as an error that needs to be
+                                * reported. The combined number of the
+                                * maximum reseed threshold times the maximum
+                                * number of Jitter RNG transient errors is
+                                * less than the reseed threshold required by
+                                * SP800-90A allowing us to treat the
+                                * transient errors as such.
+                                *
+                                * However, we mandate that at least the first
+                                * seeding operation must succeed with the
+                                * Jitter RNG.
+                                */
+                               if (!reseed || ret != -EAGAIN)
+                                       goto out;
                        }
 
                        drbg_string_fill(&data1, entropy, entropylen * 2);
@@ -1294,8 +1306,10 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
        if (IS_ENABLED(CONFIG_CRYPTO_FIPS)) {
                drbg->prev = kzalloc(drbg_sec_strength(drbg->core->flags),
                                     GFP_KERNEL);
-               if (!drbg->prev)
+               if (!drbg->prev) {
+                       ret = -ENOMEM;
                        goto fini;
+               }
                drbg->fips_primed = false;
        }
 
@@ -1492,6 +1506,8 @@ static int drbg_prepare_hrng(struct drbg_state *drbg)
        if (list_empty(&drbg->test_data.list))
                return 0;
 
+       drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+
        INIT_WORK(&drbg->seed_work, drbg_async_seed);
 
        drbg->random_ready.owner = THIS_MODULE;
@@ -1512,8 +1528,6 @@ static int drbg_prepare_hrng(struct drbg_state *drbg)
                return err;
        }
 
-       drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
-
        /*
         * Require frequent reseeds until the seed source is fully
         * initialized.
index 465a89c9d1effe3b7071a003696df6ef368d96a3..a7f45dbc4ee2893d567ea3f13aa0c61874e10b7d 100644 (file)
@@ -66,7 +66,6 @@ static int essiv_skcipher_setkey(struct crypto_skcipher *tfm,
                                 const u8 *key, unsigned int keylen)
 {
        struct essiv_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
-       SHASH_DESC_ON_STACK(desc, tctx->hash);
        u8 salt[HASH_MAX_DIGESTSIZE];
        int err;
 
@@ -78,8 +77,7 @@ static int essiv_skcipher_setkey(struct crypto_skcipher *tfm,
        if (err)
                return err;
 
-       desc->tfm = tctx->hash;
-       err = crypto_shash_digest(desc, key, keylen, salt);
+       err = crypto_shash_tfm_digest(tctx->hash, key, keylen, salt);
        if (err)
                return err;
 
index d5ebc60c51430a367fbcf8c662dead4ee2788f77..ff06a3bd1ca10c86bdebe5debd8ea077ae44e955 100644 (file)
@@ -65,6 +65,7 @@ void crypto_alg_tested(const char *name, int err);
 void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,
                          struct crypto_alg *nalg);
 void crypto_remove_final(struct list_head *list);
+void crypto_shoot_alg(struct crypto_alg *alg);
 struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
                                      u32 mask);
 void *crypto_create_tfm(struct crypto_alg *alg,
index a5ce8f96790f4a6953dba0c01a90ded7134a5a4d..b43684c0dade3aef07ab32adb0511bc0e592c560 100644 (file)
@@ -108,6 +108,7 @@ void jent_get_nstime(__u64 *out)
 struct jitterentropy {
        spinlock_t jent_lock;
        struct rand_data *entropy_collector;
+       unsigned int reset_cnt;
 };
 
 static int jent_kcapi_init(struct crypto_tfm *tfm)
@@ -142,7 +143,33 @@ static int jent_kcapi_random(struct crypto_rng *tfm,
        int ret = 0;
 
        spin_lock(&rng->jent_lock);
+
+       /* Return a permanent error in case we had too many resets in a row. */
+       if (rng->reset_cnt > (1<<10)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
        ret = jent_read_entropy(rng->entropy_collector, rdata, dlen);
+
+       /* Reset RNG in case of health failures */
+       if (ret < -1) {
+               pr_warn_ratelimited("Reset Jitter RNG due to health test failure: %s failure\n",
+                                   (ret == -2) ? "Repetition Count Test" :
+                                                 "Adaptive Proportion Test");
+
+               rng->reset_cnt++;
+
+               ret = -EAGAIN;
+       } else {
+               rng->reset_cnt = 0;
+
+               /* Convert the Jitter RNG error into a usable error code */
+               if (ret == -1)
+                       ret = -EINVAL;
+       }
+
+out:
        spin_unlock(&rng->jent_lock);
 
        return ret;
index 042157f0d28b92f4bb8d8f5f85d47d36d91235ed..57f4a1ac738bcd31ecd02a9f6210731c7a6e9911 100644 (file)
@@ -2,7 +2,7 @@
  * Non-physical true random number generator based on timing jitter --
  * Jitter RNG standalone code.
  *
- * Copyright Stephan Mueller <smueller@chronox.de>, 2015 - 2019
+ * Copyright Stephan Mueller <smueller@chronox.de>, 2015 - 2020
  *
  * Design
  * ======
@@ -47,7 +47,7 @@
 
 /*
  * This Jitterentropy RNG is based on the jitterentropy library
- * version 2.1.2 provided at http://www.chronox.de/jent.html
+ * version 2.2.0 provided at http://www.chronox.de/jent.html
  */
 
 #ifdef __OPTIMIZE__
@@ -83,6 +83,22 @@ struct rand_data {
        unsigned int memblocksize; /* Size of one memory block in bytes */
        unsigned int memaccessloops; /* Number of memory accesses per random
                                      * bit generation */
+
+       /* Repetition Count Test */
+       int rct_count;                  /* Number of stuck values */
+
+       /* Adaptive Proportion Test for a significance level of 2^-30 */
+#define JENT_APT_CUTOFF                325     /* Taken from SP800-90B sec 4.4.2 */
+#define JENT_APT_WINDOW_SIZE   512     /* Data window size */
+       /* LSB of time stamp to process */
+#define JENT_APT_LSB           16
+#define JENT_APT_WORD_MASK     (JENT_APT_LSB - 1)
+       unsigned int apt_observations;  /* Number of collected observations */
+       unsigned int apt_count;         /* APT counter */
+       unsigned int apt_base;          /* APT base reference */
+       unsigned int apt_base_set:1;    /* APT base reference set? */
+
+       unsigned int health_failure:1;  /* Permanent health failure */
 };
 
 /* Flags that can be used to initialize the RNG */
@@ -98,12 +114,201 @@ struct rand_data {
                                   * variations (2nd derivation of time is
                                   * zero). */
 #define JENT_ESTUCK            8 /* Too many stuck results during init. */
+#define JENT_EHEALTH           9 /* Health test failed during initialization */
+#define JENT_ERCT              10 /* RCT failed during initialization */
+
+#include "jitterentropy.h"
 
 /***************************************************************************
- * Helper functions
+ * Adaptive Proportion Test
+ *
+ * This test complies with SP800-90B section 4.4.2.
  ***************************************************************************/
 
-#include "jitterentropy.h"
+/**
+ * Reset the APT counter
+ *
+ * @ec [in] Reference to entropy collector
+ */
+static void jent_apt_reset(struct rand_data *ec, unsigned int delta_masked)
+{
+       /* Reset APT counter */
+       ec->apt_count = 0;
+       ec->apt_base = delta_masked;
+       ec->apt_observations = 0;
+}
+
+/**
+ * Insert a new entropy event into APT
+ *
+ * @ec [in] Reference to entropy collector
+ * @delta_masked [in] Masked time delta to process
+ */
+static void jent_apt_insert(struct rand_data *ec, unsigned int delta_masked)
+{
+       /* Initialize the base reference */
+       if (!ec->apt_base_set) {
+               ec->apt_base = delta_masked;
+               ec->apt_base_set = 1;
+               return;
+       }
+
+       if (delta_masked == ec->apt_base) {
+               ec->apt_count++;
+
+               if (ec->apt_count >= JENT_APT_CUTOFF)
+                       ec->health_failure = 1;
+       }
+
+       ec->apt_observations++;
+
+       if (ec->apt_observations >= JENT_APT_WINDOW_SIZE)
+               jent_apt_reset(ec, delta_masked);
+}
+
+/***************************************************************************
+ * Stuck Test and its use as Repetition Count Test
+ *
+ * The Jitter RNG uses an enhanced version of the Repetition Count Test
+ * (RCT) specified in SP800-90B section 4.4.1. Instead of counting identical
+ * back-to-back values, the input to the RCT is the counting of the stuck
+ * values during the generation of one Jitter RNG output block.
+ *
+ * The RCT is applied with an alpha of 2^{-30} compliant to FIPS 140-2 IG 9.8.
+ *
+ * During the counting operation, the Jitter RNG always calculates the RCT
+ * cut-off value of C. If that value exceeds the allowed cut-off value,
+ * the Jitter RNG output block will be calculated completely but discarded at
+ * the end. The caller of the Jitter RNG is informed with an error code.
+ ***************************************************************************/
+
+/**
+ * Repetition Count Test as defined in SP800-90B section 4.4.1
+ *
+ * @ec [in] Reference to entropy collector
+ * @stuck [in] Indicator whether the value is stuck
+ */
+static void jent_rct_insert(struct rand_data *ec, int stuck)
+{
+       /*
+        * If we have a count less than zero, a previous RCT round identified
+        * a failure. We will not overwrite it.
+        */
+       if (ec->rct_count < 0)
+               return;
+
+       if (stuck) {
+               ec->rct_count++;
+
+               /*
+                * The cutoff value is based on the following consideration:
+                * alpha = 2^-30 as recommended in FIPS 140-2 IG 9.8.
+                * In addition, we require an entropy value H of 1/OSR as this
+                * is the minimum entropy required to provide full entropy.
+                * Note, we collect 64 * OSR deltas for inserting them into
+                * the entropy pool which should then have (close to) 64 bits
+                * of entropy.
+                *
+                * Note, ec->rct_count (which equals to value B in the pseudo
+                * code of SP800-90B section 4.4.1) starts with zero. Hence
+                * we need to subtract one from the cutoff value as calculated
+                * following SP800-90B.
+                */
+               if ((unsigned int)ec->rct_count >= (31 * ec->osr)) {
+                       ec->rct_count = -1;
+                       ec->health_failure = 1;
+               }
+       } else {
+               ec->rct_count = 0;
+       }
+}
+
+/**
+ * Is there an RCT health test failure?
+ *
+ * @ec [in] Reference to entropy collector
+ *
+ * @return
+ *     0 No health test failure
+ *     1 Permanent health test failure
+ */
+static int jent_rct_failure(struct rand_data *ec)
+{
+       if (ec->rct_count < 0)
+               return 1;
+       return 0;
+}
+
+static inline __u64 jent_delta(__u64 prev, __u64 next)
+{
+#define JENT_UINT64_MAX                (__u64)(~((__u64) 0))
+       return (prev < next) ? (next - prev) :
+                              (JENT_UINT64_MAX - prev + 1 + next);
+}
+
+/**
+ * Stuck test by checking the:
+ *     1st derivative of the jitter measurement (time delta)
+ *     2nd derivative of the jitter measurement (delta of time deltas)
+ *     3rd derivative of the jitter measurement (delta of delta of time deltas)
+ *
+ * All values must always be non-zero.
+ *
+ * @ec [in] Reference to entropy collector
+ * @current_delta [in] Jitter time delta
+ *
+ * @return
+ *     0 jitter measurement not stuck (good bit)
+ *     1 jitter measurement stuck (reject bit)
+ */
+static int jent_stuck(struct rand_data *ec, __u64 current_delta)
+{
+       __u64 delta2 = jent_delta(ec->last_delta, current_delta);
+       __u64 delta3 = jent_delta(ec->last_delta2, delta2);
+       unsigned int delta_masked = current_delta & JENT_APT_WORD_MASK;
+
+       ec->last_delta = current_delta;
+       ec->last_delta2 = delta2;
+
+       /*
+        * Insert the result of the comparison of two back-to-back time
+        * deltas.
+        */
+       jent_apt_insert(ec, delta_masked);
+
+       if (!current_delta || !delta2 || !delta3) {
+               /* RCT with a stuck bit */
+               jent_rct_insert(ec, 1);
+               return 1;
+       }
+
+       /* RCT with a non-stuck bit */
+       jent_rct_insert(ec, 0);
+
+       return 0;
+}
+
+/**
+ * Report any health test failures
+ *
+ * @ec [in] Reference to entropy collector
+ *
+ * @return
+ *     0 No health test failure
+ *     1 Permanent health test failure
+ */
+static int jent_health_failure(struct rand_data *ec)
+{
+       /* Test is only enabled in FIPS mode */
+       if (!jent_fips_enabled())
+               return 0;
+
+       return ec->health_failure;
+}
+
+/***************************************************************************
+ * Noise sources
+ ***************************************************************************/
 
 /**
  * Update of the loop count used for the next round of
@@ -148,10 +353,6 @@ static __u64 jent_loop_shuffle(struct rand_data *ec,
        return (shuffle + (1<<min));
 }
 
-/***************************************************************************
- * Noise sources
- ***************************************************************************/
-
 /**
  * CPU Jitter noise source -- this is the noise source based on the CPU
  *                           execution time jitter
@@ -166,18 +367,19 @@ static __u64 jent_loop_shuffle(struct rand_data *ec,
  * the CPU execution time jitter. Any change to the loop in this function
  * implies that careful retesting must be done.
  *
- * Input:
- * @ec entropy collector struct
- * @time time stamp to be injected
- * @loop_cnt if a value not equal to 0 is set, use the given value as number of
- *          loops to perform the folding
+ * @ec [in] entropy collector struct
+ * @time [in] time stamp to be injected
+ * @loop_cnt [in] if a value not equal to 0 is set, use the given value as
+ *               number of loops to perform the folding
+ * @stuck [in] Is the time stamp identified as stuck?
  *
  * Output:
  * updated ec->data
  *
  * @return Number of loops the folding operation is performed
  */
-static __u64 jent_lfsr_time(struct rand_data *ec, __u64 time, __u64 loop_cnt)
+static void jent_lfsr_time(struct rand_data *ec, __u64 time, __u64 loop_cnt,
+                          int stuck)
 {
        unsigned int i;
        __u64 j = 0;
@@ -220,9 +422,17 @@ static __u64 jent_lfsr_time(struct rand_data *ec, __u64 time, __u64 loop_cnt)
                        new ^= tmp;
                }
        }
-       ec->data = new;
 
-       return fold_loop_cnt;
+       /*
+        * If the time stamp is stuck, do not finally insert the value into
+        * the entropy pool. Although this operation should not do any harm
+        * even when the time stamp has no entropy, SP800-90B requires that
+        * any conditioning operation (SP800-90B considers the LFSR to be a
+        * conditioning operation) to have an identical amount of input
+        * data according to section 3.1.5.
+        */
+       if (!stuck)
+               ec->data = new;
 }
 
 /**
@@ -243,16 +453,13 @@ static __u64 jent_lfsr_time(struct rand_data *ec, __u64 time, __u64 loop_cnt)
  * to reliably access either L3 or memory, the ec->mem memory must be quite
  * large which is usually not desirable.
  *
- * Input:
- * @ec Reference to the entropy collector with the memory access data -- if
- *     the reference to the memory block to be accessed is NULL, this noise
- *     source is disabled
- * @loop_cnt if a value not equal to 0 is set, use the given value as number of
- *          loops to perform the folding
- *
- * @return Number of memory access operations
+ * @ec [in] Reference to the entropy collector with the memory access data -- if
+ *         the reference to the memory block to be accessed is NULL, this noise
+ *         source is disabled
+ * @loop_cnt [in] if a value not equal to 0 is set, use the given value
+ *               number of loops to perform the LFSR
  */
-static unsigned int jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
+static void jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
 {
        unsigned int wrap = 0;
        __u64 i = 0;
@@ -262,7 +469,7 @@ static unsigned int jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
                jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT);
 
        if (NULL == ec || NULL == ec->mem)
-               return 0;
+               return;
        wrap = ec->memblocksize * ec->memblocks;
 
        /*
@@ -288,43 +495,11 @@ static unsigned int jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
                ec->memlocation = ec->memlocation + ec->memblocksize - 1;
                ec->memlocation = ec->memlocation % wrap;
        }
-       return i;
 }
 
 /***************************************************************************
  * Start of entropy processing logic
  ***************************************************************************/
-
-/**
- * Stuck test by checking the:
- *     1st derivation of the jitter measurement (time delta)
- *     2nd derivation of the jitter measurement (delta of time deltas)
- *     3rd derivation of the jitter measurement (delta of delta of time deltas)
- *
- * All values must always be non-zero.
- *
- * Input:
- * @ec Reference to entropy collector
- * @current_delta Jitter time delta
- *
- * @return
- *     0 jitter measurement not stuck (good bit)
- *     1 jitter measurement stuck (reject bit)
- */
-static int jent_stuck(struct rand_data *ec, __u64 current_delta)
-{
-       __s64 delta2 = ec->last_delta - current_delta;
-       __s64 delta3 = delta2 - ec->last_delta2;
-
-       ec->last_delta = current_delta;
-       ec->last_delta2 = delta2;
-
-       if (!current_delta || !delta2 || !delta3)
-               return 1;
-
-       return 0;
-}
-
 /**
  * This is the heart of the entropy generation: calculate time deltas and
  * use the CPU jitter in the time deltas. The jitter is injected into the
@@ -334,8 +509,7 @@ static int jent_stuck(struct rand_data *ec, __u64 current_delta)
  *         of this function! This can be done by calling this function
  *         and not using its result.
  *
- * Input:
- * @entropy_collector Reference to entropy collector
+ * @ec [in] Reference to entropy collector
  *
  * @return result of stuck test
  */
@@ -343,6 +517,7 @@ static int jent_measure_jitter(struct rand_data *ec)
 {
        __u64 time = 0;
        __u64 current_delta = 0;
+       int stuck;
 
        /* Invoke one noise source before time measurement to add variations */
        jent_memaccess(ec, 0);
@@ -352,22 +527,23 @@ static int jent_measure_jitter(struct rand_data *ec)
         * invocation to measure the timing variations
         */
        jent_get_nstime(&time);
-       current_delta = time - ec->prev_time;
+       current_delta = jent_delta(ec->prev_time, time);
        ec->prev_time = time;
 
+       /* Check whether we have a stuck measurement. */
+       stuck = jent_stuck(ec, current_delta);
+
        /* Now call the next noise sources which also injects the data */
-       jent_lfsr_time(ec, current_delta, 0);
+       jent_lfsr_time(ec, current_delta, 0, stuck);
 
-       /* Check whether we have a stuck measurement. */
-       return jent_stuck(ec, current_delta);
+       return stuck;
 }
 
 /**
  * Generator of one 64 bit random number
  * Function fills rand_data->data
  *
- * Input:
- * @ec Reference to entropy collector
+ * @ec [in] Reference to entropy collector
  */
 static void jent_gen_entropy(struct rand_data *ec)
 {
@@ -390,31 +566,6 @@ static void jent_gen_entropy(struct rand_data *ec)
        }
 }
 
-/**
- * The continuous test required by FIPS 140-2 -- the function automatically
- * primes the test if needed.
- *
- * Return:
- * returns normally if FIPS test passed
- * panics the kernel if FIPS test failed
- */
-static void jent_fips_test(struct rand_data *ec)
-{
-       if (!jent_fips_enabled())
-               return;
-
-       /* prime the FIPS test */
-       if (!ec->old_data) {
-               ec->old_data = ec->data;
-               jent_gen_entropy(ec);
-       }
-
-       if (ec->data == ec->old_data)
-               jent_panic("jitterentropy: Duplicate output detected\n");
-
-       ec->old_data = ec->data;
-}
-
 /**
  * Entry function: Obtain entropy for the caller.
  *
@@ -425,17 +576,18 @@ static void jent_fips_test(struct rand_data *ec)
  * This function truncates the last 64 bit entropy value output to the exact
  * size specified by the caller.
  *
- * Input:
- * @ec Reference to entropy collector
- * @data pointer to buffer for storing random data -- buffer must already
- *      exist
- * @len size of the buffer, specifying also the requested number of random
- *     in bytes
+ * @ec [in] Reference to entropy collector
+ * @data [in] pointer to buffer for storing random data -- buffer must already
+ *           exist
+ * @len [in] size of the buffer, specifying also the requested number of random
+ *          in bytes
  *
  * @return 0 when request is fulfilled or an error
  *
  * The following error codes can occur:
  *     -1      entropy_collector is NULL
+ *     -2      RCT failed
+ *     -3      APT test failed
  */
 int jent_read_entropy(struct rand_data *ec, unsigned char *data,
                      unsigned int len)
@@ -449,7 +601,42 @@ int jent_read_entropy(struct rand_data *ec, unsigned char *data,
                unsigned int tocopy;
 
                jent_gen_entropy(ec);
-               jent_fips_test(ec);
+
+               if (jent_health_failure(ec)) {
+                       int ret;
+
+                       if (jent_rct_failure(ec))
+                               ret = -2;
+                       else
+                               ret = -3;
+
+                       /*
+                        * Re-initialize the noise source
+                        *
+                        * If the health test fails, the Jitter RNG remains
+                        * in failure state and will return a health failure
+                        * during next invocation.
+                        */
+                       if (jent_entropy_init())
+                               return ret;
+
+                       /* Set APT to initial state */
+                       jent_apt_reset(ec, 0);
+                       ec->apt_base_set = 0;
+
+                       /* Set RCT to initial state */
+                       ec->rct_count = 0;
+
+                       /* Re-enable Jitter RNG */
+                       ec->health_failure = 0;
+
+                       /*
+                        * Return the health test failure status to the
+                        * caller as the generated value is not appropriate.
+                        */
+                       return ret;
+               }
+
                if ((DATA_SIZE_BITS / 8) < len)
                        tocopy = (DATA_SIZE_BITS / 8);
                else
@@ -513,11 +700,15 @@ int jent_entropy_init(void)
        int i;
        __u64 delta_sum = 0;
        __u64 old_delta = 0;
+       unsigned int nonstuck = 0;
        int time_backwards = 0;
        int count_mod = 0;
        int count_stuck = 0;
        struct rand_data ec = { 0 };
 
+       /* Required for RCT */
+       ec.osr = 1;
+
        /* We could perform statistical tests here, but the problem is
         * that we only have a few loop counts to do testing. These
         * loop counts may show some slight skew and we produce
@@ -539,8 +730,10 @@ int jent_entropy_init(void)
        /*
         * TESTLOOPCOUNT needs some loops to identify edge systems. 100 is
         * definitely too little.
+        *
+        * SP800-90B requires at least 1024 initial test cycles.
         */
-#define TESTLOOPCOUNT 300
+#define TESTLOOPCOUNT 1024
 #define CLEARCACHE 100
        for (i = 0; (TESTLOOPCOUNT + CLEARCACHE) > i; i++) {
                __u64 time = 0;
@@ -552,13 +745,13 @@ int jent_entropy_init(void)
                /* Invoke core entropy collection logic */
                jent_get_nstime(&time);
                ec.prev_time = time;
-               jent_lfsr_time(&ec, time, 0);
+               jent_lfsr_time(&ec, time, 0, 0);
                jent_get_nstime(&time2);
 
                /* test whether timer works */
                if (!time || !time2)
                        return JENT_ENOTIME;
-               delta = time2 - time;
+               delta = jent_delta(time, time2);
                /*
                 * test whether timer is fine grained enough to provide
                 * delta even when called shortly after each other -- this
@@ -581,6 +774,28 @@ int jent_entropy_init(void)
 
                if (stuck)
                        count_stuck++;
+               else {
+                       nonstuck++;
+
+                       /*
+                        * Ensure that the APT succeeded.
+                        *
+                        * With the check below that count_stuck must be less
+                        * than 10% of the overall generated raw entropy values
+                        * it is guaranteed that the APT is invoked at
+                        * floor((TESTLOOPCOUNT * 0.9) / 64) == 14 times.
+                        */
+                       if ((nonstuck % JENT_APT_WINDOW_SIZE) == 0) {
+                               jent_apt_reset(&ec,
+                                              delta & JENT_APT_WORD_MASK);
+                               if (jent_health_failure(&ec))
+                                       return JENT_EHEALTH;
+                       }
+               }
+
+               /* Validate RCT */
+               if (jent_rct_failure(&ec))
+                       return JENT_ERCT;
 
                /* test whether we have an increasing timer */
                if (!(time2 > time))
index 3c734b81b3a20108a4b7292bf17d2e6c179fcfb1..5b07a7c09296690a7195822acaaaa332d69ca430 100644 (file)
@@ -287,7 +287,7 @@ static void exit_tfm(struct crypto_skcipher *tfm)
        crypto_free_skcipher(ctx->child);
 }
 
-static void free_inst(struct skcipher_instance *inst)
+static void crypto_lrw_free(struct skcipher_instance *inst)
 {
        crypto_drop_skcipher(skcipher_instance_ctx(inst));
        kfree(inst);
@@ -400,12 +400,12 @@ static int create(struct crypto_template *tmpl, struct rtattr **tb)
        inst->alg.encrypt = encrypt;
        inst->alg.decrypt = decrypt;
 
-       inst->free = free_inst;
+       inst->free = crypto_lrw_free;
 
        err = skcipher_register_instance(tmpl, inst);
        if (err) {
 err_free_inst:
-               free_inst(inst);
+               crypto_lrw_free(inst);
        }
        return err;
 }
index 7c57b844c38275c06e48171b301b0745b83d5f2b..1d43472fecbde1967d0a1b26e814329c65c67ffb 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mm.h>
-#include <linux/cryptohash.h>
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha1_base.h>
@@ -31,10 +30,10 @@ EXPORT_SYMBOL_GPL(sha1_zero_message_hash);
 static void sha1_generic_block_fn(struct sha1_state *sst, u8 const *src,
                                  int blocks)
 {
-       u32 temp[SHA_WORKSPACE_WORDS];
+       u32 temp[SHA1_WORKSPACE_WORDS];
 
        while (blocks--) {
-               sha_transform(sst->state, src, temp);
+               sha1_transform(sst->state, src, temp);
                src += SHA1_BLOCK_SIZE;
        }
        memzero_explicit(temp, sizeof(temp));
index f2d7095d4f2d6486b65aa36ed4628d2f49ee7207..88156e3e2a33e0c24611a55b6a196c4c2de2b5ca 100644 (file)
@@ -35,27 +35,31 @@ EXPORT_SYMBOL_GPL(sha256_zero_message_hash);
 
 static int crypto_sha256_init(struct shash_desc *desc)
 {
-       return sha256_init(shash_desc_ctx(desc));
+       sha256_init(shash_desc_ctx(desc));
+       return 0;
 }
 
 static int crypto_sha224_init(struct shash_desc *desc)
 {
-       return sha224_init(shash_desc_ctx(desc));
+       sha224_init(shash_desc_ctx(desc));
+       return 0;
 }
 
 int crypto_sha256_update(struct shash_desc *desc, const u8 *data,
                          unsigned int len)
 {
-       return sha256_update(shash_desc_ctx(desc), data, len);
+       sha256_update(shash_desc_ctx(desc), data, len);
+       return 0;
 }
 EXPORT_SYMBOL(crypto_sha256_update);
 
 static int crypto_sha256_final(struct shash_desc *desc, u8 *out)
 {
        if (crypto_shash_digestsize(desc->tfm) == SHA224_DIGEST_SIZE)
-               return sha224_final(shash_desc_ctx(desc), out);
+               sha224_final(shash_desc_ctx(desc), out);
        else
-               return sha256_final(shash_desc_ctx(desc), out);
+               sha256_final(shash_desc_ctx(desc), out);
+       return 0;
 }
 
 int crypto_sha256_finup(struct shash_desc *desc, const u8 *data,
index c075b26c2a1d9f034f89e8300fea679f65882991..e6a4b5f39b8c64c247fd709cf8c1ad7dc73d3b65 100644 (file)
@@ -206,6 +206,22 @@ int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
 }
 EXPORT_SYMBOL_GPL(crypto_shash_digest);
 
+int crypto_shash_tfm_digest(struct crypto_shash *tfm, const u8 *data,
+                           unsigned int len, u8 *out)
+{
+       SHASH_DESC_ON_STACK(desc, tfm);
+       int err;
+
+       desc->tfm = tfm;
+
+       err = crypto_shash_digest(desc, data, len, out);
+
+       shash_desc_zero(desc);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(crypto_shash_tfm_digest);
+
 static int shash_default_export(struct shash_desc *desc, void *out)
 {
        memcpy(out, shash_desc_ctx(desc), crypto_shash_descsize(desc->tfm));
index 6d8cea94b3cfbbc276014c3b831c640a99e0fd6f..3565f3b863a61c70a868bb01b683555b0db203eb 100644 (file)
@@ -322,7 +322,7 @@ static void exit_tfm(struct crypto_skcipher *tfm)
        crypto_free_cipher(ctx->tweak);
 }
 
-static void free_inst(struct skcipher_instance *inst)
+static void crypto_xts_free(struct skcipher_instance *inst)
 {
        crypto_drop_skcipher(skcipher_instance_ctx(inst));
        kfree(inst);
@@ -434,12 +434,12 @@ static int create(struct crypto_template *tmpl, struct rtattr **tb)
        inst->alg.encrypt = encrypt;
        inst->alg.decrypt = decrypt;
 
-       inst->free = free_inst;
+       inst->free = crypto_xts_free;
 
        err = skcipher_register_instance(tmpl, inst);
        if (err) {
 err_free_inst:
-               free_inst(inst);
+               crypto_xts_free(inst);
        }
        return err;
 }
index dee9999382134dc02834d9815ee02eeb6d0bf1d6..5e2bfbcf526f3d756bbad2c7545e025d48091c00 100644 (file)
@@ -1041,7 +1041,7 @@ static int acpi_lpss_do_suspend_late(struct device *dev)
 {
        int ret;
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        ret = pm_generic_suspend_late(dev);
@@ -1093,6 +1093,9 @@ static int acpi_lpss_resume_early(struct device *dev)
        if (pdata->dev_desc->resume_from_noirq)
                return 0;
 
+       if (dev_pm_skip_resume(dev))
+               return 0;
+
        return acpi_lpss_do_resume_early(dev);
 }
 
@@ -1102,12 +1105,9 @@ static int acpi_lpss_resume_noirq(struct device *dev)
        int ret;
 
        /* Follow acpi_subsys_resume_noirq(). */
-       if (dev_pm_may_skip_resume(dev))
+       if (dev_pm_skip_resume(dev))
                return 0;
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
-               pm_runtime_set_active(dev);
-
        ret = pm_generic_resume_noirq(dev);
        if (ret)
                return ret;
@@ -1169,7 +1169,7 @@ static int acpi_lpss_poweroff_late(struct device *dev)
 {
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        if (pdata->dev_desc->resume_from_noirq)
@@ -1182,7 +1182,7 @@ static int acpi_lpss_poweroff_noirq(struct device *dev)
 {
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        if (pdata->dev_desc->resume_from_noirq) {
index 33a4bcdaa4d79a6431238422eba9e7b8412def20..7d45cce0c3c187ffc33e4f7d3526d181acf1088b 100644 (file)
@@ -624,7 +624,7 @@ static int acpi_tad_probe(struct platform_device *pdev)
         */
        device_init_wakeup(dev, true);
        dev_pm_set_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
-                                    DPM_FLAG_LEAVE_SUSPENDED);
+                                    DPM_FLAG_MAY_SKIP_RESUME);
        /*
         * The platform bus type layer tells the ACPI PM domain powers up the
         * device, so set the runtime PM status of it to "active".
index 5abca09455ad8e082254c27adec4cff3fa774b6b..81bf71b10d44fc2acf43a14312bc9e56985ed9ec 100644 (file)
@@ -168,12 +168,6 @@ int ghes_estatus_pool_init(int num_ghes)
        if (!addr)
                goto err_pool_alloc;
 
-       /*
-        * New allocation must be visible in all pgd before it can be found by
-        * an NMI allocating from the pool.
-        */
-       vmalloc_sync_mappings();
-
        rc = gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1);
        if (rc)
                goto err_pool_add;
index 01962c63a7116ee3b58df8ccdb52818d6d9f1019..f2d0e5915dab5975a37dec195dcd1538a0657721 100644 (file)
@@ -394,7 +394,7 @@ static int __init gtdt_sbsa_gwdt_init(void)
         */
        ret = acpi_gtdt_init(table, &timer_count);
        if (ret || !timer_count)
-               return ret;
+               goto out_put_gtdt;
 
        for_each_platform_timer(platform_timer) {
                if (is_non_secure_watchdog(platform_timer)) {
@@ -408,6 +408,8 @@ static int __init gtdt_sbsa_gwdt_init(void)
        if (gwdt_count)
                pr_info("found %d SBSA generic Watchdog(s).\n", gwdt_count);
 
+out_put_gtdt:
+       acpi_put_table(table);
        return ret;
 }
 
index 7d04424189dfbbf7f837cdb0cb6427f8d947261f..28a6b387e80e2870ba5cbab7f8cf640788a1f111 100644 (file)
@@ -299,61 +299,8 @@ out:
        return status;
 }
 
-struct iort_workaround_oem_info {
-       char oem_id[ACPI_OEM_ID_SIZE + 1];
-       char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
-       u32 oem_revision;
-};
-
-static bool apply_id_count_workaround;
-
-static struct iort_workaround_oem_info wa_info[] __initdata = {
-       {
-               .oem_id         = "HISI  ",
-               .oem_table_id   = "HIP07   ",
-               .oem_revision   = 0,
-       }, {
-               .oem_id         = "HISI  ",
-               .oem_table_id   = "HIP08   ",
-               .oem_revision   = 0,
-       }
-};
-
-static void __init
-iort_check_id_count_workaround(struct acpi_table_header *tbl)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wa_info); i++) {
-               if (!memcmp(wa_info[i].oem_id, tbl->oem_id, ACPI_OEM_ID_SIZE) &&
-                   !memcmp(wa_info[i].oem_table_id, tbl->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
-                   wa_info[i].oem_revision == tbl->oem_revision) {
-                       apply_id_count_workaround = true;
-                       pr_warn(FW_BUG "ID count for ID mapping entry is wrong, applying workaround\n");
-                       break;
-               }
-       }
-}
-
-static inline u32 iort_get_map_max(struct acpi_iort_id_mapping *map)
-{
-       u32 map_max = map->input_base + map->id_count;
-
-       /*
-        * The IORT specification revision D (Section 3, table 4, page 9) says
-        * Number of IDs = The number of IDs in the range minus one, but the
-        * IORT code ignored the "minus one", and some firmware did that too,
-        * so apply a workaround here to keep compatible with both the spec
-        * compliant and non-spec compliant firmwares.
-        */
-       if (apply_id_count_workaround)
-               map_max--;
-
-       return map_max;
-}
-
 static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
-                      u32 *rid_out)
+                      u32 *rid_out, bool check_overlap)
 {
        /* Single mapping does not care for input id */
        if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
@@ -368,10 +315,37 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
                return -ENXIO;
        }
 
-       if (rid_in < map->input_base || rid_in > iort_get_map_max(map))
+       if (rid_in < map->input_base ||
+           (rid_in > map->input_base + map->id_count))
                return -ENXIO;
 
+       if (check_overlap) {
+               /*
+                * We already found a mapping for this input ID at the end of
+                * another region. If it coincides with the start of this
+                * region, we assume the prior match was due to the off-by-1
+                * issue mentioned below, and allow it to be superseded.
+                * Otherwise, things are *really* broken, and we just disregard
+                * duplicate matches entirely to retain compatibility.
+                */
+               pr_err(FW_BUG "[map %p] conflicting mapping for input ID 0x%x\n",
+                      map, rid_in);
+               if (rid_in != map->input_base)
+                       return -ENXIO;
+
+               pr_err(FW_BUG "applying workaround.\n");
+       }
+
        *rid_out = map->output_base + (rid_in - map->input_base);
+
+       /*
+        * Due to confusion regarding the meaning of the id_count field (which
+        * carries the number of IDs *minus 1*), we may have to disregard this
+        * match if it is at the end of the range, and overlaps with the start
+        * of another one.
+        */
+       if (map->id_count > 0 && rid_in == map->input_base + map->id_count)
+               return -EAGAIN;
        return 0;
 }
 
@@ -414,6 +388,7 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
 static int iort_get_id_mapping_index(struct acpi_iort_node *node)
 {
        struct acpi_iort_smmu_v3 *smmu;
+       struct acpi_iort_pmcg *pmcg;
 
        switch (node->type) {
        case ACPI_IORT_NODE_SMMU_V3:
@@ -441,6 +416,10 @@ static int iort_get_id_mapping_index(struct acpi_iort_node *node)
 
                return smmu->id_mapping_index;
        case ACPI_IORT_NODE_PMCG:
+               pmcg = (struct acpi_iort_pmcg *)node->node_data;
+               if (pmcg->overflow_gsiv || node->mapping_count == 0)
+                       return -EINVAL;
+
                return 0;
        default:
                return -EINVAL;
@@ -456,7 +435,8 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node,
        /* Parse the ID mapping tree to find specified node type */
        while (node) {
                struct acpi_iort_id_mapping *map;
-               int i, index;
+               int i, index, rc = 0;
+               u32 out_ref = 0, map_id = id;
 
                if (IORT_TYPE_MASK(node->type) & type_mask) {
                        if (id_out)
@@ -490,15 +470,18 @@ static struct acpi_iort_node *iort_node_map_id(struct acpi_iort_node *node,
                        if (i == index)
                                continue;
 
-                       if (!iort_id_map(map, node->type, id, &id))
+                       rc = iort_id_map(map, node->type, map_id, &id, out_ref);
+                       if (!rc)
                                break;
+                       if (rc == -EAGAIN)
+                               out_ref = map->output_reference;
                }
 
-               if (i == node->mapping_count)
+               if (i == node->mapping_count && !out_ref)
                        goto fail_map;
 
                node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
-                                   map->output_reference);
+                                   rc ? out_ref : map->output_reference);
        }
 
 fail_map:
@@ -789,15 +772,6 @@ void acpi_configure_pmsi_domain(struct device *dev)
                dev_set_msi_domain(dev, msi_domain);
 }
 
-static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias,
-                                       void *data)
-{
-       u32 *rid = data;
-
-       *rid = alias;
-       return 0;
-}
-
 #ifdef CONFIG_IOMMU_API
 static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev)
 {
@@ -1148,13 +1122,10 @@ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
        else
                size = 1ULL << 32;
 
-       if (dev_is_pci(dev)) {
-               ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size);
-               if (ret == -ENODEV)
-                       ret = rc_dma_get_range(dev, &size);
-       } else {
-               ret = nc_dma_get_range(dev, &size);
-       }
+       ret = acpi_dma_get_range(dev, &dmaaddr, &offset, &size);
+       if (ret == -ENODEV)
+               ret = dev_is_pci(dev) ? rc_dma_get_range(dev, &size)
+                                     : nc_dma_get_range(dev, &size);
 
        if (!ret) {
                /*
@@ -1692,6 +1663,10 @@ void __init acpi_iort_init(void)
 {
        acpi_status status;
 
+       /* iort_table will be used at runtime after the iort init,
+        * so we don't need to call acpi_put_table() to release
+        * the IORT table mapping.
+        */
        status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
        if (ACPI_FAILURE(status)) {
                if (status != AE_NOT_FOUND) {
@@ -1703,6 +1678,5 @@ void __init acpi_iort_init(void)
                return;
        }
 
-       iort_check_id_count_workaround(iort_table);
        iort_init_platform_devices();
 }
index 5832bc10aca8d5ff93354df2bcf59d00fd67640d..b44b12a931e7eb2511190027092015f6060143be 100644 (file)
@@ -1084,7 +1084,7 @@ int acpi_subsys_suspend_late(struct device *dev)
 {
        int ret;
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        ret = pm_generic_suspend_late(dev);
@@ -1100,10 +1100,8 @@ int acpi_subsys_suspend_noirq(struct device *dev)
 {
        int ret;
 
-       if (dev_pm_smart_suspend_and_suspended(dev)) {
-               dev->power.may_skip_resume = true;
+       if (dev_pm_skip_suspend(dev))
                return 0;
-       }
 
        ret = pm_generic_suspend_noirq(dev);
        if (ret)
@@ -1116,8 +1114,8 @@ int acpi_subsys_suspend_noirq(struct device *dev)
         * acpi_subsys_complete() to take care of fixing up the device's state
         * anyway, if need be.
         */
-       dev->power.may_skip_resume = device_may_wakeup(dev) ||
-                                       !device_can_wakeup(dev);
+       if (device_can_wakeup(dev) && !device_may_wakeup(dev))
+               dev->power.may_skip_resume = false;
 
        return 0;
 }
@@ -1129,17 +1127,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
  */
 static int acpi_subsys_resume_noirq(struct device *dev)
 {
-       if (dev_pm_may_skip_resume(dev))
+       if (dev_pm_skip_resume(dev))
                return 0;
 
-       /*
-        * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
-        * during system suspend, so update their runtime PM status to "active"
-        * as they will be put into D0 going forward.
-        */
-       if (dev_pm_smart_suspend_and_suspended(dev))
-               pm_runtime_set_active(dev);
-
        return pm_generic_resume_noirq(dev);
 }
 
@@ -1153,7 +1143,12 @@ static int acpi_subsys_resume_noirq(struct device *dev)
  */
 static int acpi_subsys_resume_early(struct device *dev)
 {
-       int ret = acpi_dev_resume(dev);
+       int ret;
+
+       if (dev_pm_skip_resume(dev))
+               return 0;
+
+       ret = acpi_dev_resume(dev);
        return ret ? ret : pm_generic_resume_early(dev);
 }
 
@@ -1218,7 +1213,7 @@ static int acpi_subsys_poweroff_late(struct device *dev)
 {
        int ret;
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        ret = pm_generic_poweroff_late(dev);
@@ -1234,7 +1229,7 @@ static int acpi_subsys_poweroff_late(struct device *dev)
  */
 static int acpi_subsys_poweroff_noirq(struct device *dev)
 {
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        return pm_generic_poweroff_noirq(dev);
index 8f467d929f4b25930d95ca18a625cd106f8998ac..04ce2b96c3da95853274a12b67e13373d4208200 100644 (file)
@@ -2020,7 +2020,7 @@ bool acpi_ec_dispatch_gpe(void)
         */
        ret = acpi_dispatch_gpe(NULL, first_ec->gpe);
        if (ret == ACPI_INTERRUPT_HANDLED) {
-               pm_pr_dbg("EC GPE dispatched\n");
+               pm_pr_dbg("ACPI EC GPE dispatched\n");
 
                /* Flush the event and query workqueues. */
                acpi_ec_flush_work();
index 2a0e1c04e31239e23b2206e857693989e26bc29b..aff13bf4d94705e0f94467f4c7c372a58024dea7 100644 (file)
@@ -992,23 +992,31 @@ static bool acpi_s2idle_wake(void)
                 * wakeup is pending anyway and the SCI is not the source of
                 * it).
                 */
-               if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
+               if (irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
+                       pm_pr_dbg("Wakeup unrelated to ACPI SCI\n");
                        return true;
+               }
 
                /*
                 * If the status bit of any enabled fixed event is set, the
                 * wakeup is regarded as valid.
                 */
-               if (acpi_any_fixed_event_status_set())
+               if (acpi_any_fixed_event_status_set()) {
+                       pm_pr_dbg("ACPI fixed event wakeup\n");
                        return true;
+               }
 
                /* Check wakeups from drivers sharing the SCI. */
-               if (acpi_check_wakeup_handlers())
+               if (acpi_check_wakeup_handlers()) {
+                       pm_pr_dbg("ACPI custom handler wakeup\n");
                        return true;
+               }
 
                /* Check non-EC GPE wakeups and dispatch the EC GPE. */
-               if (acpi_ec_dispatch_gpe())
+               if (acpi_ec_dispatch_gpe()) {
+                       pm_pr_dbg("ACPI non-EC GPE wakeup\n");
                        return true;
+               }
 
                /*
                 * Cancel the SCI wakeup and process all pending events in case
@@ -1027,8 +1035,10 @@ static bool acpi_s2idle_wake(void)
                 * are pending here, they must be resulting from the processing
                 * of EC events above or coming from somewhere else.
                 */
-               if (pm_wakeup_pending())
+               if (pm_wakeup_pending()) {
+                       pm_pr_dbg("Wakeup after ACPI Notify sync\n");
                        return true;
+               }
 
                rearm_wake_irq(acpi_sci_irq);
        }
index beca5f91bb4c7c8bed8a40c118717363981da495..69361ec43db57c98a1b1c60b928515853259d91d 100644 (file)
@@ -5209,7 +5209,7 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
  *     sata_link_init_spd - Initialize link->sata_spd_limit
  *     @link: Link to configure sata_spd_limit for
  *
- *     Initialize @link->[hw_]sata_spd_limit to the currently
+ *     Initialize ``link->[hw_]sata_spd_limit`` to the currently
  *     configured value.
  *
  *     LOCKING:
index 0cad34f1eedea8dcc88c51ab371aa4e1e978ac8a..edb6fd2032a1b3192461634a2bae11a9211a8b51 100644 (file)
@@ -1393,7 +1393,7 @@ static void device_release(struct kobject *kobj)
        else if (dev->class && dev->class->dev_release)
                dev->class->dev_release(dev);
        else
-               WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/kobject.txt.\n",
+               WARN(1, KERN_ERR "Device '%s' does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
                        dev_name(dev));
        kfree(p);
 }
@@ -3915,6 +3915,7 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
        else
                dev->fwnode = fwnode;
 }
+EXPORT_SYMBOL_GPL(set_secondary_fwnode);
 
 /**
  * device_set_of_node_from_dev - reuse device-tree node of another device
index 10d7e818e118d32e9ca37429b10805099c467add..5b02f69769e86f67a8d73835bfc28c6ad4d79ca1 100644 (file)
@@ -415,6 +415,9 @@ static ssize_t node_read_meminfo(struct device *dev,
                       "Node %d AnonPages:      %8lu kB\n"
                       "Node %d Shmem:          %8lu kB\n"
                       "Node %d KernelStack:    %8lu kB\n"
+#ifdef CONFIG_SHADOW_CALL_STACK
+                      "Node %d ShadowCallStack:%8lu kB\n"
+#endif
                       "Node %d PageTables:     %8lu kB\n"
                       "Node %d NFS_Unstable:   %8lu kB\n"
                       "Node %d Bounce:         %8lu kB\n"
@@ -438,8 +441,11 @@ static ssize_t node_read_meminfo(struct device *dev,
                       nid, K(node_page_state(pgdat, NR_ANON_MAPPED)),
                       nid, K(i.sharedram),
                       nid, sum_zone_node_page_state(nid, NR_KERNEL_STACK_KB),
+#ifdef CONFIG_SHADOW_CALL_STACK
+                      nid, sum_zone_node_page_state(nid, NR_KERNEL_SCS_KB),
+#endif
                       nid, K(sum_zone_node_page_state(nid, NR_PAGETABLE)),
-                      nid, K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
+                      nid, 0UL,
                       nid, K(sum_zone_node_page_state(nid, NR_BOUNCE)),
                       nid, K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
                       nid, K(sreclaimable +
index b27d0f6c18c9c0eeec0c46ade6db1c338ec0b1a4..615c6b06b42768b494700718db08a295fa97f736 100644 (file)
@@ -147,7 +147,8 @@ EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);
  * request_irq() APIs. This is the same as platform_get_irq(), except that it
  * does not print an error message if an IRQ can not be obtained.
  *
- * Example:
+ * For example::
+ *
  *             int irq = platform_get_irq_optional(pdev, 0);
  *             if (irq < 0)
  *                     return irq;
@@ -226,7 +227,8 @@ EXPORT_SYMBOL_GPL(platform_get_irq_optional);
  * IRQ fails. Device drivers should check the return value for errors so as to
  * not pass a negative integer value to the request_irq() APIs.
  *
- * Example:
+ * For example::
+ *
  *             int irq = platform_get_irq(pdev, 0);
  *             if (irq < 0)
  *                     return irq;
index 0e07e17c2def339448a2a6ae41d496f703b81472..bb98b813554fdd12619b978fbbb2c73b02343e28 100644 (file)
@@ -562,72 +562,26 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
 /*------------------------- Resume routines -------------------------*/
 
 /**
- * suspend_event - Return a "suspend" message for given "resume" one.
- * @resume_msg: PM message representing a system-wide resume transition.
- */
-static pm_message_t suspend_event(pm_message_t resume_msg)
-{
-       switch (resume_msg.event) {
-       case PM_EVENT_RESUME:
-               return PMSG_SUSPEND;
-       case PM_EVENT_THAW:
-       case PM_EVENT_RESTORE:
-               return PMSG_FREEZE;
-       case PM_EVENT_RECOVER:
-               return PMSG_HIBERNATE;
-       }
-       return PMSG_ON;
-}
-
-/**
- * dev_pm_may_skip_resume - System-wide device resume optimization check.
+ * dev_pm_skip_resume - System-wide device resume optimization check.
  * @dev: Target device.
  *
- * Checks whether or not the device may be left in suspend after a system-wide
- * transition to the working state.
+ * Return:
+ * - %false if the transition under way is RESTORE.
+ * - Return value of dev_pm_skip_suspend() if the transition under way is THAW.
+ * - The logical negation of %power.must_resume otherwise (that is, when the
+ *   transition under way is RESUME).
  */
-bool dev_pm_may_skip_resume(struct device *dev)
+bool dev_pm_skip_resume(struct device *dev)
 {
-       return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE;
-}
-
-static pm_callback_t dpm_subsys_resume_noirq_cb(struct device *dev,
-                                               pm_message_t state,
-                                               const char **info_p)
-{
-       pm_callback_t callback;
-       const char *info;
-
-       if (dev->pm_domain) {
-               info = "noirq power domain ";
-               callback = pm_noirq_op(&dev->pm_domain->ops, state);
-       } else if (dev->type && dev->type->pm) {
-               info = "noirq type ";
-               callback = pm_noirq_op(dev->type->pm, state);
-       } else if (dev->class && dev->class->pm) {
-               info = "noirq class ";
-               callback = pm_noirq_op(dev->class->pm, state);
-       } else if (dev->bus && dev->bus->pm) {
-               info = "noirq bus ";
-               callback = pm_noirq_op(dev->bus->pm, state);
-       } else {
-               return NULL;
-       }
+       if (pm_transition.event == PM_EVENT_RESTORE)
+               return false;
 
-       if (info_p)
-               *info_p = info;
+       if (pm_transition.event == PM_EVENT_THAW)
+               return dev_pm_skip_suspend(dev);
 
-       return callback;
+       return !dev->power.must_resume;
 }
 
-static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
-                                                pm_message_t state,
-                                                const char **info_p);
-
-static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
-                                               pm_message_t state,
-                                               const char **info_p);
-
 /**
  * device_resume_noirq - Execute a "noirq resume" callback for given device.
  * @dev: Device to handle.
@@ -639,8 +593,8 @@ static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
  */
 static int device_resume_noirq(struct device *dev, pm_message_t state, bool async)
 {
-       pm_callback_t callback;
-       const char *info;
+       pm_callback_t callback = NULL;
+       const char *info = NULL;
        bool skip_resume;
        int error = 0;
 
@@ -656,37 +610,41 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
        if (!dpm_wait_for_superior(dev, async))
                goto Out;
 
-       skip_resume = dev_pm_may_skip_resume(dev);
+       skip_resume = dev_pm_skip_resume(dev);
+       /*
+        * If the driver callback is skipped below or by the middle layer
+        * callback and device_resume_early() also skips the driver callback for
+        * this device later, it needs to appear as "suspended" to PM-runtime,
+        * so change its status accordingly.
+        *
+        * Otherwise, the device is going to be resumed, so set its PM-runtime
+        * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set
+        * to avoid confusing drivers that don't use it.
+        */
+       if (skip_resume)
+               pm_runtime_set_suspended(dev);
+       else if (dev_pm_skip_suspend(dev))
+               pm_runtime_set_active(dev);
 
-       callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
+       if (dev->pm_domain) {
+               info = "noirq power domain ";
+               callback = pm_noirq_op(&dev->pm_domain->ops, state);
+       } else if (dev->type && dev->type->pm) {
+               info = "noirq type ";
+               callback = pm_noirq_op(dev->type->pm, state);
+       } else if (dev->class && dev->class->pm) {
+               info = "noirq class ";
+               callback = pm_noirq_op(dev->class->pm, state);
+       } else if (dev->bus && dev->bus->pm) {
+               info = "noirq bus ";
+               callback = pm_noirq_op(dev->bus->pm, state);
+       }
        if (callback)
                goto Run;
 
        if (skip_resume)
                goto Skip;
 
-       if (dev_pm_smart_suspend_and_suspended(dev)) {
-               pm_message_t suspend_msg = suspend_event(state);
-
-               /*
-                * If "freeze" callbacks have been skipped during a transition
-                * related to hibernation, the subsequent "thaw" callbacks must
-                * be skipped too or bad things may happen.  Otherwise, resume
-                * callbacks are going to be run for the device, so its runtime
-                * PM status must be changed to reflect the new state after the
-                * transition under way.
-                */
-               if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) &&
-                   !dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) {
-                       if (state.event == PM_EVENT_THAW) {
-                               skip_resume = true;
-                               goto Skip;
-                       } else {
-                               pm_runtime_set_active(dev);
-                       }
-               }
-       }
-
        if (dev->driver && dev->driver->pm) {
                info = "noirq driver ";
                callback = pm_noirq_op(dev->driver->pm, state);
@@ -698,20 +656,6 @@ Run:
 Skip:
        dev->power.is_noirq_suspended = false;
 
-       if (skip_resume) {
-               /* Make the next phases of resume skip the device. */
-               dev->power.is_late_suspended = false;
-               dev->power.is_suspended = false;
-               /*
-                * The device is going to be left in suspend, but it might not
-                * have been in runtime suspend before the system suspended, so
-                * its runtime PM status needs to be updated to avoid confusing
-                * the runtime PM framework when runtime PM is enabled for the
-                * device again.
-                */
-               pm_runtime_set_suspended(dev);
-       }
-
 Out:
        complete_all(&dev->power.completion);
        TRACE_RESUME(error);
@@ -810,35 +754,6 @@ void dpm_resume_noirq(pm_message_t state)
        cpuidle_resume();
 }
 
-static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
-                                               pm_message_t state,
-                                               const char **info_p)
-{
-       pm_callback_t callback;
-       const char *info;
-
-       if (dev->pm_domain) {
-               info = "early power domain ";
-               callback = pm_late_early_op(&dev->pm_domain->ops, state);
-       } else if (dev->type && dev->type->pm) {
-               info = "early type ";
-               callback = pm_late_early_op(dev->type->pm, state);
-       } else if (dev->class && dev->class->pm) {
-               info = "early class ";
-               callback = pm_late_early_op(dev->class->pm, state);
-       } else if (dev->bus && dev->bus->pm) {
-               info = "early bus ";
-               callback = pm_late_early_op(dev->bus->pm, state);
-       } else {
-               return NULL;
-       }
-
-       if (info_p)
-               *info_p = info;
-
-       return callback;
-}
-
 /**
  * device_resume_early - Execute an "early resume" callback for given device.
  * @dev: Device to handle.
@@ -849,8 +764,8 @@ static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
  */
 static int device_resume_early(struct device *dev, pm_message_t state, bool async)
 {
-       pm_callback_t callback;
-       const char *info;
+       pm_callback_t callback = NULL;
+       const char *info = NULL;
        int error = 0;
 
        TRACE_DEVICE(dev);
@@ -865,17 +780,37 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
        if (!dpm_wait_for_superior(dev, async))
                goto Out;
 
-       callback = dpm_subsys_resume_early_cb(dev, state, &info);
+       if (dev->pm_domain) {
+               info = "early power domain ";
+               callback = pm_late_early_op(&dev->pm_domain->ops, state);
+       } else if (dev->type && dev->type->pm) {
+               info = "early type ";
+               callback = pm_late_early_op(dev->type->pm, state);
+       } else if (dev->class && dev->class->pm) {
+               info = "early class ";
+               callback = pm_late_early_op(dev->class->pm, state);
+       } else if (dev->bus && dev->bus->pm) {
+               info = "early bus ";
+               callback = pm_late_early_op(dev->bus->pm, state);
+       }
+       if (callback)
+               goto Run;
+
+       if (dev_pm_skip_resume(dev))
+               goto Skip;
 
-       if (!callback && dev->driver && dev->driver->pm) {
+       if (dev->driver && dev->driver->pm) {
                info = "early driver ";
                callback = pm_late_early_op(dev->driver->pm, state);
        }
 
+Run:
        error = dpm_run_callback(callback, dev, state, info);
+
+Skip:
        dev->power.is_late_suspended = false;
 
- Out:
+Out:
        TRACE_RESUME(error);
 
        pm_runtime_enable(dev);
@@ -1245,61 +1180,6 @@ static void dpm_superior_set_must_resume(struct device *dev)
        device_links_read_unlock(idx);
 }
 
-static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
-                                                pm_message_t state,
-                                                const char **info_p)
-{
-       pm_callback_t callback;
-       const char *info;
-
-       if (dev->pm_domain) {
-               info = "noirq power domain ";
-               callback = pm_noirq_op(&dev->pm_domain->ops, state);
-       } else if (dev->type && dev->type->pm) {
-               info = "noirq type ";
-               callback = pm_noirq_op(dev->type->pm, state);
-       } else if (dev->class && dev->class->pm) {
-               info = "noirq class ";
-               callback = pm_noirq_op(dev->class->pm, state);
-       } else if (dev->bus && dev->bus->pm) {
-               info = "noirq bus ";
-               callback = pm_noirq_op(dev->bus->pm, state);
-       } else {
-               return NULL;
-       }
-
-       if (info_p)
-               *info_p = info;
-
-       return callback;
-}
-
-static bool device_must_resume(struct device *dev, pm_message_t state,
-                              bool no_subsys_suspend_noirq)
-{
-       pm_message_t resume_msg = resume_event(state);
-
-       /*
-        * If all of the device driver's "noirq", "late" and "early" callbacks
-        * are invoked directly by the core, the decision to allow the device to
-        * stay in suspend can be based on its current runtime PM status and its
-        * wakeup settings.
-        */
-       if (no_subsys_suspend_noirq &&
-           !dpm_subsys_suspend_late_cb(dev, state, NULL) &&
-           !dpm_subsys_resume_early_cb(dev, resume_msg, NULL) &&
-           !dpm_subsys_resume_noirq_cb(dev, resume_msg, NULL))
-               return !pm_runtime_status_suspended(dev) &&
-                       (resume_msg.event != PM_EVENT_RESUME ||
-                        (device_can_wakeup(dev) && !device_may_wakeup(dev)));
-
-       /*
-        * The only safe strategy here is to require that if the device may not
-        * be left in suspend, resume callbacks must be invoked for it.
-        */
-       return !dev->power.may_skip_resume;
-}
-
 /**
  * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
  * @dev: Device to handle.
@@ -1311,9 +1191,8 @@ static bool device_must_resume(struct device *dev, pm_message_t state,
  */
 static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
 {
-       pm_callback_t callback;
-       const char *info;
-       bool no_subsys_cb = false;
+       pm_callback_t callback = NULL;
+       const char *info = NULL;
        int error = 0;
 
        TRACE_DEVICE(dev);
@@ -1327,13 +1206,23 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
-       callback = dpm_subsys_suspend_noirq_cb(dev, state, &info);
+       if (dev->pm_domain) {
+               info = "noirq power domain ";
+               callback = pm_noirq_op(&dev->pm_domain->ops, state);
+       } else if (dev->type && dev->type->pm) {
+               info = "noirq type ";
+               callback = pm_noirq_op(dev->type->pm, state);
+       } else if (dev->class && dev->class->pm) {
+               info = "noirq class ";
+               callback = pm_noirq_op(dev->class->pm, state);
+       } else if (dev->bus && dev->bus->pm) {
+               info = "noirq bus ";
+               callback = pm_noirq_op(dev->bus->pm, state);
+       }
        if (callback)
                goto Run;
 
-       no_subsys_cb = !dpm_subsys_suspend_late_cb(dev, state, NULL);
-
-       if (dev_pm_smart_suspend_and_suspended(dev) && no_subsys_cb)
+       if (dev_pm_skip_suspend(dev))
                goto Skip;
 
        if (dev->driver && dev->driver->pm) {
@@ -1351,13 +1240,16 @@ Run:
 Skip:
        dev->power.is_noirq_suspended = true;
 
-       if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) {
-               dev->power.must_resume = dev->power.must_resume ||
-                               atomic_read(&dev->power.usage_count) > 1 ||
-                               device_must_resume(dev, state, no_subsys_cb);
-       } else {
+       /*
+        * Skipping the resume of devices that were in use right before the
+        * system suspend (as indicated by their PM-runtime usage counters)
+        * would be suboptimal.  Also resume them if doing that is not allowed
+        * to be skipped.
+        */
+       if (atomic_read(&dev->power.usage_count) > 1 ||
+           !(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
+             dev->power.may_skip_resume))
                dev->power.must_resume = true;
-       }
 
        if (dev->power.must_resume)
                dpm_superior_set_must_resume(dev);
@@ -1474,35 +1366,6 @@ static void dpm_propagate_wakeup_to_parent(struct device *dev)
        spin_unlock_irq(&parent->power.lock);
 }
 
-static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
-                                               pm_message_t state,
-                                               const char **info_p)
-{
-       pm_callback_t callback;
-       const char *info;
-
-       if (dev->pm_domain) {
-               info = "late power domain ";
-               callback = pm_late_early_op(&dev->pm_domain->ops, state);
-       } else if (dev->type && dev->type->pm) {
-               info = "late type ";
-               callback = pm_late_early_op(dev->type->pm, state);
-       } else if (dev->class && dev->class->pm) {
-               info = "late class ";
-               callback = pm_late_early_op(dev->class->pm, state);
-       } else if (dev->bus && dev->bus->pm) {
-               info = "late bus ";
-               callback = pm_late_early_op(dev->bus->pm, state);
-       } else {
-               return NULL;
-       }
-
-       if (info_p)
-               *info_p = info;
-
-       return callback;
-}
-
 /**
  * __device_suspend_late - Execute a "late suspend" callback for given device.
  * @dev: Device to handle.
@@ -1513,8 +1376,8 @@ static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
  */
 static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
 {
-       pm_callback_t callback;
-       const char *info;
+       pm_callback_t callback = NULL;
+       const char *info = NULL;
        int error = 0;
 
        TRACE_DEVICE(dev);
@@ -1535,12 +1398,23 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
-       callback = dpm_subsys_suspend_late_cb(dev, state, &info);
+       if (dev->pm_domain) {
+               info = "late power domain ";
+               callback = pm_late_early_op(&dev->pm_domain->ops, state);
+       } else if (dev->type && dev->type->pm) {
+               info = "late type ";
+               callback = pm_late_early_op(dev->type->pm, state);
+       } else if (dev->class && dev->class->pm) {
+               info = "late class ";
+               callback = pm_late_early_op(dev->class->pm, state);
+       } else if (dev->bus && dev->bus->pm) {
+               info = "late bus ";
+               callback = pm_late_early_op(dev->bus->pm, state);
+       }
        if (callback)
                goto Run;
 
-       if (dev_pm_smart_suspend_and_suspended(dev) &&
-           !dpm_subsys_suspend_noirq_cb(dev, state, NULL))
+       if (dev_pm_skip_suspend(dev))
                goto Skip;
 
        if (dev->driver && dev->driver->pm) {
@@ -1766,7 +1640,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
                dev->power.direct_complete = false;
        }
 
-       dev->power.may_skip_resume = false;
+       dev->power.may_skip_resume = true;
        dev->power.must_resume = false;
 
        dpm_watchdog_set(&wd, dev);
@@ -1970,7 +1844,7 @@ unlock:
        spin_lock_irq(&dev->power.lock);
        dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
                (ret > 0 || dev->power.no_pm_callbacks) &&
-               !dev_pm_test_driver_flags(dev, DPM_FLAG_NEVER_SKIP);
+               !dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
        spin_unlock_irq(&dev->power.lock);
        return 0;
 }
@@ -2128,7 +2002,7 @@ void device_pm_check_callbacks(struct device *dev)
        spin_unlock_irq(&dev->power.lock);
 }
 
-bool dev_pm_smart_suspend_and_suspended(struct device *dev)
+bool dev_pm_skip_suspend(struct device *dev)
 {
        return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
                pm_runtime_status_suspended(dev);
index 99c7da112c95c70557a79e0bf4825ca2c1b5e897..9f62790f644c5350f804c9b36dcb01748b64b2f6 100644 (file)
@@ -523,13 +523,11 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
  repeat:
        retval = rpm_check_suspend_allowed(dev);
-
        if (retval < 0)
-               ;       /* Conditions are wrong. */
+               goto out;       /* Conditions are wrong. */
 
        /* Synchronous suspends are not allowed in the RPM_RESUMING state. */
-       else if (dev->power.runtime_status == RPM_RESUMING &&
-           !(rpmflags & RPM_ASYNC))
+       if (dev->power.runtime_status == RPM_RESUMING && !(rpmflags & RPM_ASYNC))
                retval = -EAGAIN;
        if (retval)
                goto out;
index 2b99fe1eb207dabd9737b28f11686ff514f942f6..24d25cf8ab14870d3e883e39b3201c86cb6974f6 100644 (file)
@@ -666,7 +666,7 @@ int dpm_sysfs_add(struct device *dev)
        if (rc)
                return rc;
 
-       if (pm_runtime_callbacks_present(dev)) {
+       if (!pm_runtime_has_no_callbacks(dev)) {
                rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
                if (rc)
                        goto err_out;
@@ -709,7 +709,7 @@ int dpm_sysfs_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
        if (rc)
                return rc;
 
-       if (pm_runtime_callbacks_present(dev)) {
+       if (!pm_runtime_has_no_callbacks(dev)) {
                rc = sysfs_group_change_owner(
                        &dev->kobj, &pm_runtime_attr_group, kuid, kgid);
                if (rc)
index e72843fe41dfeea565b2c0dc2722688561670228..089e5dc7144a6a1a090cbe61dd04fcc0b9fd6c38 100644 (file)
@@ -227,6 +227,9 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
        if (*ppos < 0 || !count)
                return -EINVAL;
 
+       if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
+               count = PAGE_SIZE << (MAX_ORDER - 1);
+
        buf = kmalloc(count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
@@ -371,6 +374,9 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
        if (*ppos < 0 || !count)
                return -EINVAL;
 
+       if (count > (PAGE_SIZE << (MAX_ORDER - 1)))
+               count = PAGE_SIZE << (MAX_ORDER - 1);
+
        buf = kmalloc(count, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
index 008f8da69d973183635768b52cc7e1b0119363dd..62b95a9212ae1cc31f96bbb7d2081264822c45be 100644 (file)
@@ -246,6 +246,63 @@ static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
        .max_raw_write = I2C_SMBUS_BLOCK_MAX,
 };
 
+static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
+                                     size_t count)
+{
+       struct device *dev = context;
+       struct i2c_client *i2c = to_i2c_client(dev);
+
+       if (count < 2)
+               return -EINVAL;
+
+       count--;
+       return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
+                                             (u8 *)data + 1);
+}
+
+static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
+                                    size_t reg_size, void *val,
+                                    size_t val_size)
+{
+       struct device *dev = context;
+       struct i2c_client *i2c = to_i2c_client(dev);
+       int ret, count, len = val_size;
+
+       if (reg_size != 2)
+               return -EINVAL;
+
+       ret = i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
+                                       ((u16 *)reg)[0] >> 8);
+       if (ret < 0)
+               return ret;
+
+       count = 0;
+       do {
+               /* Current Address Read */
+               ret = i2c_smbus_read_byte(i2c);
+               if (ret < 0)
+                       break;
+
+               *((u8 *)val++) = ret;
+               count++;
+               len--;
+       } while (len > 0);
+
+       if (count == val_size)
+               return 0;
+       else if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
+       .write = regmap_i2c_smbus_i2c_write_reg16,
+       .read = regmap_i2c_smbus_i2c_read_reg16,
+       .max_raw_read = I2C_SMBUS_BLOCK_MAX,
+       .max_raw_write = I2C_SMBUS_BLOCK_MAX,
+};
+
 static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
                                        const struct regmap_config *config)
 {
@@ -255,6 +312,10 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
                 i2c_check_functionality(i2c->adapter,
                                         I2C_FUNC_SMBUS_I2C_BLOCK))
                return &regmap_i2c_smbus_i2c_block;
+       else if (config->val_bits == 8 && config->reg_bits == 16 &&
+               i2c_check_functionality(i2c->adapter,
+                                       I2C_FUNC_SMBUS_I2C_BLOCK))
+               return &regmap_i2c_smbus_i2c_block_reg16;
        else if (config->val_bits == 16 && config->reg_bits == 8 &&
                 i2c_check_functionality(i2c->adapter,
                                         I2C_FUNC_SMBUS_WORD_DATA))
index 3d64c9331a82aa61cdab5be03fca8c5c08b4aea6..4340e1d268b65ac1717860d917a5723231482891 100644 (file)
@@ -541,8 +541,9 @@ static const struct irq_domain_ops regmap_domain_ops = {
 };
 
 /**
- * regmap_add_irq_chip() - Use standard regmap IRQ controller handling
+ * regmap_add_irq_chip_np() - Use standard regmap IRQ controller handling
  *
+ * @np: The device_node where the IRQ domain should be added to.
  * @map: The regmap for the device.
  * @irq: The IRQ the device uses to signal interrupts.
  * @irq_flags: The IRQF_ flags to use for the primary interrupt.
@@ -556,9 +557,10 @@ static const struct irq_domain_ops regmap_domain_ops = {
  * register cache.  The chip driver is responsible for restoring the
  * register values used by the IRQ controller over suspend and resume.
  */
-int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
-                       int irq_base, const struct regmap_irq_chip *chip,
-                       struct regmap_irq_chip_data **data)
+int regmap_add_irq_chip_np(struct device_node *np, struct regmap *map, int irq,
+                          int irq_flags, int irq_base,
+                          const struct regmap_irq_chip *chip,
+                          struct regmap_irq_chip_data **data)
 {
        struct regmap_irq_chip_data *d;
        int i;
@@ -769,12 +771,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        }
 
        if (irq_base)
-               d->domain = irq_domain_add_legacy(map->dev->of_node,
-                                                 chip->num_irqs, irq_base, 0,
-                                                 &regmap_domain_ops, d);
+               d->domain = irq_domain_add_legacy(np, chip->num_irqs, irq_base,
+                                                 0, &regmap_domain_ops, d);
        else
-               d->domain = irq_domain_add_linear(map->dev->of_node,
-                                                 chip->num_irqs,
+               d->domain = irq_domain_add_linear(np, chip->num_irqs,
                                                  &regmap_domain_ops, d);
        if (!d->domain) {
                dev_err(map->dev, "Failed to create IRQ domain\n");
@@ -808,6 +808,30 @@ err_alloc:
        kfree(d);
        return ret;
 }
+EXPORT_SYMBOL_GPL(regmap_add_irq_chip_np);
+
+/**
+ * regmap_add_irq_chip() - Use standard regmap IRQ controller handling
+ *
+ * @map: The regmap for the device.
+ * @irq: The IRQ the device uses to signal interrupts.
+ * @irq_flags: The IRQF_ flags to use for the primary interrupt.
+ * @irq_base: Allocate at specific IRQ number if irq_base > 0.
+ * @chip: Configuration for the interrupt controller.
+ * @data: Runtime data structure for the controller, allocated on success.
+ *
+ * Returns 0 on success or an errno on failure.
+ *
+ * This is the same as regmap_add_irq_chip_np, except that the device
+ * node of the regmap is used.
+ */
+int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
+                       int irq_base, const struct regmap_irq_chip *chip,
+                       struct regmap_irq_chip_data **data)
+{
+       return regmap_add_irq_chip_np(map->dev->of_node, map, irq, irq_flags,
+                                     irq_base, chip, data);
+}
 EXPORT_SYMBOL_GPL(regmap_add_irq_chip);
 
 /**
@@ -875,9 +899,10 @@ static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data)
 }
 
 /**
- * devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip()
+ * devm_regmap_add_irq_chip_np() - Resource manager regmap_add_irq_chip_np()
  *
  * @dev: The device pointer on which irq_chip belongs to.
+ * @np: The device_node where the IRQ domain should be added to.
  * @map: The regmap for the device.
  * @irq: The IRQ the device uses to signal interrupts
  * @irq_flags: The IRQF_ flags to use for the primary interrupt.
@@ -890,10 +915,11 @@ static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data)
  * The &regmap_irq_chip_data will be automatically released when the device is
  * unbound.
  */
-int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
-                            int irq_flags, int irq_base,
-                            const struct regmap_irq_chip *chip,
-                            struct regmap_irq_chip_data **data)
+int devm_regmap_add_irq_chip_np(struct device *dev, struct device_node *np,
+                               struct regmap *map, int irq, int irq_flags,
+                               int irq_base,
+                               const struct regmap_irq_chip *chip,
+                               struct regmap_irq_chip_data **data)
 {
        struct regmap_irq_chip_data **ptr, *d;
        int ret;
@@ -903,8 +929,8 @@ int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
        if (!ptr)
                return -ENOMEM;
 
-       ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base,
-                                 chip, &d);
+       ret = regmap_add_irq_chip_np(np, map, irq, irq_flags, irq_base,
+                                    chip, &d);
        if (ret < 0) {
                devres_free(ptr);
                return ret;
@@ -915,6 +941,32 @@ int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
        *data = d;
        return 0;
 }
+EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip_np);
+
+/**
+ * devm_regmap_add_irq_chip() - Resource manager regmap_add_irq_chip()
+ *
+ * @dev: The device pointer on which irq_chip belongs to.
+ * @map: The regmap for the device.
+ * @irq: The IRQ the device uses to signal interrupts
+ * @irq_flags: The IRQF_ flags to use for the primary interrupt.
+ * @irq_base: Allocate at specific IRQ number if irq_base > 0.
+ * @chip: Configuration for the interrupt controller.
+ * @data: Runtime data structure for the controller, allocated on success
+ *
+ * Returns 0 on success or an errno on failure.
+ *
+ * The &regmap_irq_chip_data will be automatically released when the device is
+ * unbound.
+ */
+int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
+                            int irq_flags, int irq_base,
+                            const struct regmap_irq_chip *chip,
+                            struct regmap_irq_chip_data **data)
+{
+       return devm_regmap_add_irq_chip_np(dev, map->dev->of_node, map, irq,
+                                          irq_flags, irq_base, chip, data);
+}
 EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip);
 
 /**
index 59f911e577192257667ab394038fa2ff56f3167f..c472f624382d092c59328942e200fa784e3eee68 100644 (file)
@@ -827,6 +827,7 @@ struct regmap *__regmap_init(struct device *dev,
        } else if (!bus->read || !bus->write) {
                map->reg_read = _regmap_bus_reg_read;
                map->reg_write = _regmap_bus_reg_write;
+               map->reg_update_bits = bus->reg_update_bits;
 
                map->defer_caching = false;
                goto skip_format_initialization;
@@ -2936,6 +2937,28 @@ int regmap_update_bits_base(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits_base);
 
+/**
+ * regmap_test_bits() - Check if all specified bits are set in a register.
+ *
+ * @map: Register map to operate on
+ * @reg: Register to read from
+ * @bits: Bits to test
+ *
+ * Returns -1 if the underlying regmap_read() fails, 0 if at least one of the
+ * tested bits is not set and 1 if all tested bits are set.
+ */
+int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits)
+{
+       unsigned int val, ret;
+
+       ret = regmap_read(map, reg, &val);
+       if (ret)
+               return ret;
+
+       return (val & bits) == bits;
+}
+EXPORT_SYMBOL_GPL(regmap_test_bits);
+
 void regmap_async_complete_cb(struct regmap_async *async, int ret)
 {
        struct regmap *map = async->map;
index de8d3543e8fe347be2ffc2f3afdedd4ef84fd35c..2079937ddb51c19367be841481f895da181759ac 100644 (file)
@@ -726,6 +726,54 @@ void software_node_unregister_nodes(const struct software_node *nodes)
 }
 EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
 
+/**
+ * software_node_register_node_group - Register a group of software nodes
+ * @node_group: NULL terminated array of software node pointers to be registered
+ *
+ * Register multiple software nodes at once.
+ */
+int software_node_register_node_group(const struct software_node **node_group)
+{
+       unsigned int i;
+       int ret;
+
+       if (!node_group)
+               return 0;
+
+       for (i = 0; node_group[i]; i++) {
+               ret = software_node_register(node_group[i]);
+               if (ret) {
+                       software_node_unregister_node_group(node_group);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(software_node_register_node_group);
+
+/**
+ * software_node_unregister_node_group - Unregister a group of software nodes
+ * @node_group: NULL terminated array of software node pointers to be unregistered
+ *
+ * Unregister multiple software nodes at once.
+ */
+void software_node_unregister_node_group(const struct software_node **node_group)
+{
+       struct swnode *swnode;
+       unsigned int i;
+
+       if (!node_group)
+               return;
+
+       for (i = 0; node_group[i]; i++) {
+               swnode = software_node_to_swnode(node_group[i]);
+               if (swnode)
+                       fwnode_remove_software_node(&swnode->fwnode);
+       }
+}
+EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
+
 /**
  * software_node_register - Register static software node
  * @node: The software node to be registered
index 15e99697234a2cfa6f4e7e859750cf237bb79c97..df53dca5d02c7e76fb4551587ea19dcce8a6200c 100644 (file)
@@ -396,9 +396,7 @@ static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
        bytes = sizeof(struct page *)*want;
        new_pages = kzalloc(bytes, GFP_NOIO | __GFP_NOWARN);
        if (!new_pages) {
-               new_pages = __vmalloc(bytes,
-                               GFP_NOIO | __GFP_ZERO,
-                               PAGE_KERNEL);
+               new_pages = __vmalloc(bytes, GFP_NOIO | __GFP_ZERO);
                if (!new_pages)
                        return NULL;
        }
index da693e6a834e583f47403cbd0f54c3f5908f441c..d89c25ba3b89a8d984e2a71e287251fd7ce2adcf 100644 (file)
@@ -919,7 +919,7 @@ static void loop_unprepare_queue(struct loop_device *lo)
 
 static int loop_kthread_worker_fn(void *worker_ptr)
 {
-       current->flags |= PF_LESS_THROTTLE | PF_MEMALLOC_NOIO;
+       current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;
        return kthread_worker_fn(worker_ptr);
 }
 
index 1a8564a79d8dca27b8462d7fe114d35c183c1a60..5ee8e3fae55161b0c36b9ad0a9ea453426745219 100644 (file)
@@ -37,19 +37,16 @@ static void zcomp_strm_free(struct zcomp_strm *zstrm)
        if (!IS_ERR_OR_NULL(zstrm->tfm))
                crypto_free_comp(zstrm->tfm);
        free_pages((unsigned long)zstrm->buffer, 1);
-       kfree(zstrm);
+       zstrm->tfm = NULL;
+       zstrm->buffer = NULL;
 }
 
 /*
- * allocate new zcomp_strm structure with ->tfm initialized by
- * backend, return NULL on error
+ * Initialize zcomp_strm structure with ->tfm initialized by backend, and
+ * ->buffer. Return a negative value on error.
  */
-static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
+static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp)
 {
-       struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
-       if (!zstrm)
-               return NULL;
-
        zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0);
        /*
         * allocate 2 pages. 1 for compressed data, plus 1 extra for the
@@ -58,9 +55,9 @@ static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
        zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
        if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) {
                zcomp_strm_free(zstrm);
-               zstrm = NULL;
+               return -ENOMEM;
        }
-       return zstrm;
+       return 0;
 }
 
 bool zcomp_available_algorithm(const char *comp)
@@ -113,12 +110,13 @@ ssize_t zcomp_available_show(const char *comp, char *buf)
 
 struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
 {
-       return *get_cpu_ptr(comp->stream);
+       local_lock(&comp->stream->lock);
+       return this_cpu_ptr(comp->stream);
 }
 
 void zcomp_stream_put(struct zcomp *comp)
 {
-       put_cpu_ptr(comp->stream);
+       local_unlock(&comp->stream->lock);
 }
 
 int zcomp_compress(struct zcomp_strm *zstrm,
@@ -159,17 +157,15 @@ int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
 {
        struct zcomp *comp = hlist_entry(node, struct zcomp, node);
        struct zcomp_strm *zstrm;
+       int ret;
 
-       if (WARN_ON(*per_cpu_ptr(comp->stream, cpu)))
-               return 0;
+       zstrm = per_cpu_ptr(comp->stream, cpu);
+       local_lock_init(&zstrm->lock);
 
-       zstrm = zcomp_strm_alloc(comp);
-       if (IS_ERR_OR_NULL(zstrm)) {
+       ret = zcomp_strm_init(zstrm, comp);
+       if (ret)
                pr_err("Can't allocate a compression stream\n");
-               return -ENOMEM;
-       }
-       *per_cpu_ptr(comp->stream, cpu) = zstrm;
-       return 0;
+       return ret;
 }
 
 int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
@@ -177,10 +173,8 @@ int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
        struct zcomp *comp = hlist_entry(node, struct zcomp, node);
        struct zcomp_strm *zstrm;
 
-       zstrm = *per_cpu_ptr(comp->stream, cpu);
-       if (!IS_ERR_OR_NULL(zstrm))
-               zcomp_strm_free(zstrm);
-       *per_cpu_ptr(comp->stream, cpu) = NULL;
+       zstrm = per_cpu_ptr(comp->stream, cpu);
+       zcomp_strm_free(zstrm);
        return 0;
 }
 
@@ -188,7 +182,7 @@ static int zcomp_init(struct zcomp *comp)
 {
        int ret;
 
-       comp->stream = alloc_percpu(struct zcomp_strm *);
+       comp->stream = alloc_percpu(struct zcomp_strm);
        if (!comp->stream)
                return -ENOMEM;
 
index 1806475b919df74d6d4f8ce4c611fd486cc0c6c0..40f6420f4b2e92d6129e4ba37c9daa5b3e229810 100644 (file)
@@ -5,8 +5,11 @@
 
 #ifndef _ZCOMP_H_
 #define _ZCOMP_H_
+#include <linux/local_lock.h>
 
 struct zcomp_strm {
+       /* The members ->buffer and ->tfm are protected by ->lock. */
+       local_lock_t lock;
        /* compression/decompression buffer */
        void *buffer;
        struct crypto_comp *tfm;
@@ -14,7 +17,7 @@ struct zcomp_strm {
 
 /* dynamic per-device compression frontend */
 struct zcomp {
-       struct zcomp_strm __percpu *stream;
+       struct zcomp_strm __percpu *stream;
        const char *name;
        struct hlist_node node;
 };
index 0f3a020703ab233d871752f9c6f6d9bce7408f42..a296f85264331d43c8a3f3807736c2d0d8e6b9a4 100644 (file)
@@ -355,31 +355,31 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
 
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
        /* Marvell SD8688 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
        /* Marvell SD8787 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
        /* Marvell SD8787 Bluetooth AMP device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT_AMP),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
        /* Marvell SD8797 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
        /* Marvell SD8887 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
        /* Marvell SD8897 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
        /* Marvell SD8977 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9146),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8977 },
        /* Marvell SD8987 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x914A),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8987 },
        /* Marvell SD8997 Bluetooth device */
-       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
+       { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_BT),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
 
        { }     /* Terminating entry */
index 519788c442ca3b7138839f5e4c10105c2f449f82..bff095be2f97114c9d540b153b54b018314da1ba 100644 (file)
@@ -51,9 +51,9 @@ static const struct btmtksdio_data mt7668_data = {
 };
 
 static const struct sdio_device_id btmtksdio_table[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7663),
         .driver_data = (kernel_ulong_t)&mt7663_data },
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7668),
         .driver_data = (kernel_ulong_t)&mt7668_data },
        { }     /* Terminating entry */
 };
index 9bc46da8d77a85d4d7fead53f910f103b43651c2..ac00d78ee9cc68b9ba118f6e23f388a293a31060 100644 (file)
@@ -474,6 +474,19 @@ config HW_RANDOM_KEYSTONE
        help
          This option enables Keystone's hardware random generator.
 
+config HW_RANDOM_CCTRNG
+       tristate "Arm CryptoCell True Random Number Generator support"
+       depends on HAS_IOMEM && OF
+       help
+         Say 'Y' to enable the True Random Number Generator driver for the
+         Arm TrustZone CryptoCell family of processors.
+         Currently the CryptoCell 713 and 703 are supported.
+         The driver is supported only in SoC where Trusted Execution
+         Environment is not used.
+         Choose 'M' to compile this driver as a module. The module
+         will be called cctrng.
+         If unsure, say 'N'.
+
 endif # HW_RANDOM
 
 config UML_RANDOM
index a7801b49ce6c0f9859dc6eb7dc091ab7ed142572..2c672473534513fe99994384df0f7563f107c243 100644 (file)
@@ -41,3 +41,4 @@ obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o
 obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o
 obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
 obj-$(CONFIG_HW_RANDOM_NPCM) += npcm-rng.o
+obj-$(CONFIG_HW_RANDOM_CCTRNG) += cctrng.o
diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c
new file mode 100644 (file)
index 0000000..619148f
--- /dev/null
@@ -0,0 +1,735 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/workqueue.h>
+#include <linux/circ_buf.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/bitfield.h>
+#include <linux/fips.h>
+
+#include "cctrng.h"
+
+#define CC_REG_LOW(name)  (name ## _BIT_SHIFT)
+#define CC_REG_HIGH(name) (CC_REG_LOW(name) + name ## _BIT_SIZE - 1)
+#define CC_GENMASK(name)  GENMASK(CC_REG_HIGH(name), CC_REG_LOW(name))
+
+#define CC_REG_FLD_GET(reg_name, fld_name, reg_val)     \
+       (FIELD_GET(CC_GENMASK(CC_ ## reg_name ## _ ## fld_name), reg_val))
+
+#define CC_HW_RESET_LOOP_COUNT 10
+#define CC_TRNG_SUSPEND_TIMEOUT 3000
+
+/* data circular buffer in words must be:
+ *  - of a power-of-2 size (limitation of circ_buf.h macros)
+ *  - at least 6, the size generated in the EHR according to HW implementation
+ */
+#define CCTRNG_DATA_BUF_WORDS 32
+
+/* The timeout for the TRNG operation should be calculated with the formula:
+ * Timeout = EHR_NUM * VN_COEFF * EHR_LENGTH * SAMPLE_CNT * SCALE_VALUE
+ * while:
+ *  - SAMPLE_CNT is input value from the characterisation process
+ *  - all the rest are constants
+ */
+#define EHR_NUM 1
+#define VN_COEFF 4
+#define EHR_LENGTH CC_TRNG_EHR_IN_BITS
+#define SCALE_VALUE 2
+#define CCTRNG_TIMEOUT(smpl_cnt) \
+       (EHR_NUM * VN_COEFF * EHR_LENGTH * smpl_cnt * SCALE_VALUE)
+
+struct cctrng_drvdata {
+       struct platform_device *pdev;
+       void __iomem *cc_base;
+       struct clk *clk;
+       struct hwrng rng;
+       u32 active_rosc;
+       /* Sampling interval for each ring oscillator:
+        * count of ring oscillator cycles between consecutive bits sampling.
+        * Value of 0 indicates non-valid rosc
+        */
+       u32 smpl_ratio[CC_TRNG_NUM_OF_ROSCS];
+
+       u32 data_buf[CCTRNG_DATA_BUF_WORDS];
+       struct circ_buf circ;
+       struct work_struct compwork;
+       struct work_struct startwork;
+
+       /* pending_hw - 1 when HW is pending, 0 when it is idle */
+       atomic_t pending_hw;
+
+       /* protects against multiple concurrent consumers of data_buf */
+       spinlock_t read_lock;
+};
+
+
+/* functions for write/read CC registers */
+static inline void cc_iowrite(struct cctrng_drvdata *drvdata, u32 reg, u32 val)
+{
+       iowrite32(val, (drvdata->cc_base + reg));
+}
+static inline u32 cc_ioread(struct cctrng_drvdata *drvdata, u32 reg)
+{
+       return ioread32(drvdata->cc_base + reg);
+}
+
+
+static int cc_trng_pm_get(struct device *dev)
+{
+       int rc = 0;
+
+       rc = pm_runtime_get_sync(dev);
+
+       /* pm_runtime_get_sync() can return 1 as a valid return code */
+       return (rc == 1 ? 0 : rc);
+}
+
+static void cc_trng_pm_put_suspend(struct device *dev)
+{
+       int rc = 0;
+
+       pm_runtime_mark_last_busy(dev);
+       rc = pm_runtime_put_autosuspend(dev);
+       if (rc)
+               dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc);
+}
+
+static int cc_trng_pm_init(struct cctrng_drvdata *drvdata)
+{
+       struct device *dev = &(drvdata->pdev->dev);
+
+       /* must be before the enabling to avoid redundant suspending */
+       pm_runtime_set_autosuspend_delay(dev, CC_TRNG_SUSPEND_TIMEOUT);
+       pm_runtime_use_autosuspend(dev);
+       /* set us as active - note we won't do PM ops until cc_trng_pm_go()! */
+       return pm_runtime_set_active(dev);
+}
+
+static void cc_trng_pm_go(struct cctrng_drvdata *drvdata)
+{
+       struct device *dev = &(drvdata->pdev->dev);
+
+       /* enable the PM module*/
+       pm_runtime_enable(dev);
+}
+
+static void cc_trng_pm_fini(struct cctrng_drvdata *drvdata)
+{
+       struct device *dev = &(drvdata->pdev->dev);
+
+       pm_runtime_disable(dev);
+}
+
+
+static inline int cc_trng_parse_sampling_ratio(struct cctrng_drvdata *drvdata)
+{
+       struct device *dev = &(drvdata->pdev->dev);
+       struct device_node *np = drvdata->pdev->dev.of_node;
+       int rc;
+       int i;
+       /* ret will be set to 0 if at least one rosc has (sampling ratio > 0) */
+       int ret = -EINVAL;
+
+       rc = of_property_read_u32_array(np, "arm,rosc-ratio",
+                                       drvdata->smpl_ratio,
+                                       CC_TRNG_NUM_OF_ROSCS);
+       if (rc) {
+               /* arm,rosc-ratio was not found in device tree */
+               return rc;
+       }
+
+       /* verify that at least one rosc has (sampling ratio > 0) */
+       for (i = 0; i < CC_TRNG_NUM_OF_ROSCS; ++i) {
+               dev_dbg(dev, "rosc %d sampling ratio %u",
+                       i, drvdata->smpl_ratio[i]);
+
+               if (drvdata->smpl_ratio[i] > 0)
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+static int cc_trng_change_rosc(struct cctrng_drvdata *drvdata)
+{
+       struct device *dev = &(drvdata->pdev->dev);
+
+       dev_dbg(dev, "cctrng change rosc (was %d)\n", drvdata->active_rosc);
+       drvdata->active_rosc += 1;
+
+       while (drvdata->active_rosc < CC_TRNG_NUM_OF_ROSCS) {
+               if (drvdata->smpl_ratio[drvdata->active_rosc] > 0)
+                       return 0;
+
+               drvdata->active_rosc += 1;
+       }
+       return -EINVAL;
+}
+
+
+static void cc_trng_enable_rnd_source(struct cctrng_drvdata *drvdata)
+{
+       u32 max_cycles;
+
+       /* Set watchdog threshold to maximal allowed time (in CPU cycles) */
+       max_cycles = CCTRNG_TIMEOUT(drvdata->smpl_ratio[drvdata->active_rosc]);
+       cc_iowrite(drvdata, CC_RNG_WATCHDOG_VAL_REG_OFFSET, max_cycles);
+
+       /* enable the RND source */
+       cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0x1);
+
+       /* unmask RNG interrupts */
+       cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, (u32)~CC_RNG_INT_MASK);
+}
+
+
+/* increase circular data buffer index (head/tail) */
+static inline void circ_idx_inc(int *idx, int bytes)
+{
+       *idx += (bytes + 3) >> 2;
+       *idx &= (CCTRNG_DATA_BUF_WORDS - 1);
+}
+
+static inline size_t circ_buf_space(struct cctrng_drvdata *drvdata)
+{
+       return CIRC_SPACE(drvdata->circ.head,
+                         drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS);
+
+}
+
+static int cctrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+       /* current implementation ignores "wait" */
+
+       struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)rng->priv;
+       struct device *dev = &(drvdata->pdev->dev);
+       u32 *buf = (u32 *)drvdata->circ.buf;
+       size_t copied = 0;
+       size_t cnt_w;
+       size_t size;
+       size_t left;
+
+       if (!spin_trylock(&drvdata->read_lock)) {
+               /* concurrent consumers from data_buf cannot be served */
+               dev_dbg_ratelimited(dev, "unable to hold lock\n");
+               return 0;
+       }
+
+       /* copy till end of data buffer (without wrap back) */
+       cnt_w = CIRC_CNT_TO_END(drvdata->circ.head,
+                               drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS);
+       size = min((cnt_w<<2), max);
+       memcpy(data, &(buf[drvdata->circ.tail]), size);
+       copied = size;
+       circ_idx_inc(&drvdata->circ.tail, size);
+       /* copy rest of data in data buffer */
+       left = max - copied;
+       if (left > 0) {
+               cnt_w = CIRC_CNT(drvdata->circ.head,
+                                drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS);
+               size = min((cnt_w<<2), left);
+               memcpy(data, &(buf[drvdata->circ.tail]), size);
+               copied += size;
+               circ_idx_inc(&drvdata->circ.tail, size);
+       }
+
+       spin_unlock(&drvdata->read_lock);
+
+       if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) {
+               if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) {
+                       /* re-check space in buffer to avoid potential race */
+                       if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) {
+                               /* increment device's usage counter */
+                               int rc = cc_trng_pm_get(dev);
+
+                               if (rc) {
+                                       dev_err(dev,
+                                               "cc_trng_pm_get returned %x\n",
+                                               rc);
+                                       return rc;
+                               }
+
+                               /* schedule execution of deferred work handler
+                                * for filling of data buffer
+                                */
+                               schedule_work(&drvdata->startwork);
+                       } else {
+                               atomic_set(&drvdata->pending_hw, 0);
+                       }
+               }
+       }
+
+       return copied;
+}
+
+static void cc_trng_hw_trigger(struct cctrng_drvdata *drvdata)
+{
+       u32 tmp_smpl_cnt = 0;
+       struct device *dev = &(drvdata->pdev->dev);
+
+       dev_dbg(dev, "cctrng hw trigger.\n");
+
+       /* enable the HW RND clock */
+       cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1);
+
+       /* do software reset */
+       cc_iowrite(drvdata, CC_RNG_SW_RESET_REG_OFFSET, 0x1);
+       /* in order to verify that the reset has completed,
+        * the sample count need to be verified
+        */
+       do {
+               /* enable the HW RND clock   */
+               cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1);
+
+               /* set sampling ratio (rng_clocks) between consecutive bits */
+               cc_iowrite(drvdata, CC_SAMPLE_CNT1_REG_OFFSET,
+                          drvdata->smpl_ratio[drvdata->active_rosc]);
+
+               /* read the sampling ratio  */
+               tmp_smpl_cnt = cc_ioread(drvdata, CC_SAMPLE_CNT1_REG_OFFSET);
+
+       } while (tmp_smpl_cnt != drvdata->smpl_ratio[drvdata->active_rosc]);
+
+       /* disable the RND source for setting new parameters in HW */
+       cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0);
+
+       cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, 0xFFFFFFFF);
+
+       cc_iowrite(drvdata, CC_TRNG_CONFIG_REG_OFFSET, drvdata->active_rosc);
+
+       /* Debug Control register: set to 0 - no bypasses */
+       cc_iowrite(drvdata, CC_TRNG_DEBUG_CONTROL_REG_OFFSET, 0);
+
+       cc_trng_enable_rnd_source(drvdata);
+}
+
+static void cc_trng_compwork_handler(struct work_struct *w)
+{
+       u32 isr = 0;
+       u32 ehr_valid = 0;
+       struct cctrng_drvdata *drvdata =
+                       container_of(w, struct cctrng_drvdata, compwork);
+       struct device *dev = &(drvdata->pdev->dev);
+       int i;
+
+       /* stop DMA and the RNG source */
+       cc_iowrite(drvdata, CC_RNG_DMA_ENABLE_REG_OFFSET, 0);
+       cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0);
+
+       /* read RNG_ISR and check for errors */
+       isr = cc_ioread(drvdata, CC_RNG_ISR_REG_OFFSET);
+       ehr_valid = CC_REG_FLD_GET(RNG_ISR, EHR_VALID, isr);
+       dev_dbg(dev, "Got RNG_ISR=0x%08X (EHR_VALID=%u)\n", isr, ehr_valid);
+
+       if (fips_enabled && CC_REG_FLD_GET(RNG_ISR, CRNGT_ERR, isr)) {
+               fips_fail_notify();
+               /* FIPS error is fatal */
+               panic("Got HW CRNGT error while fips is enabled!\n");
+       }
+
+       /* Clear all pending RNG interrupts */
+       cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, isr);
+
+
+       if (!ehr_valid) {
+               /* in case of AUTOCORR/TIMEOUT error, try the next ROSC */
+               if (CC_REG_FLD_GET(RNG_ISR, AUTOCORR_ERR, isr) ||
+                               CC_REG_FLD_GET(RNG_ISR, WATCHDOG, isr)) {
+                       dev_dbg(dev, "cctrng autocorr/timeout error.\n");
+                       goto next_rosc;
+               }
+
+               /* in case of VN error, ignore it */
+       }
+
+       /* read EHR data from registers */
+       for (i = 0; i < CC_TRNG_EHR_IN_WORDS; i++) {
+               /* calc word ptr in data_buf */
+               u32 *buf = (u32 *)drvdata->circ.buf;
+
+               buf[drvdata->circ.head] = cc_ioread(drvdata,
+                               CC_EHR_DATA_0_REG_OFFSET + (i*sizeof(u32)));
+
+               /* EHR_DATA registers are cleared on read. In case 0 value was
+                * returned, restart the entropy collection.
+                */
+               if (buf[drvdata->circ.head] == 0) {
+                       dev_dbg(dev, "Got 0 value in EHR. active_rosc %u\n",
+                               drvdata->active_rosc);
+                       goto next_rosc;
+               }
+
+               circ_idx_inc(&drvdata->circ.head, 1<<2);
+       }
+
+       atomic_set(&drvdata->pending_hw, 0);
+
+       /* continue to fill data buffer if needed */
+       if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) {
+               if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) {
+                       /* Re-enable rnd source */
+                       cc_trng_enable_rnd_source(drvdata);
+                       return;
+               }
+       }
+
+       cc_trng_pm_put_suspend(dev);
+
+       dev_dbg(dev, "compwork handler done\n");
+       return;
+
+next_rosc:
+       if ((circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) &&
+                       (cc_trng_change_rosc(drvdata) == 0)) {
+               /* trigger trng hw with next rosc */
+               cc_trng_hw_trigger(drvdata);
+       } else {
+               atomic_set(&drvdata->pending_hw, 0);
+               cc_trng_pm_put_suspend(dev);
+       }
+}
+
+static irqreturn_t cc_isr(int irq, void *dev_id)
+{
+       struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)dev_id;
+       struct device *dev = &(drvdata->pdev->dev);
+       u32 irr;
+
+       /* if driver suspended return, probably shared interrupt */
+       if (pm_runtime_suspended(dev))
+               return IRQ_NONE;
+
+       /* read the interrupt status */
+       irr = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET);
+       dev_dbg(dev, "Got IRR=0x%08X\n", irr);
+
+       if (irr == 0) /* Probably shared interrupt line */
+               return IRQ_NONE;
+
+       /* clear interrupt - must be before processing events */
+       cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, irr);
+
+       /* RNG interrupt - most probable */
+       if (irr & CC_HOST_RNG_IRQ_MASK) {
+               /* Mask RNG interrupts - will be unmasked in deferred work */
+               cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, 0xFFFFFFFF);
+
+               /* We clear RNG interrupt here,
+                * to avoid it from firing as we'll unmask RNG interrupts.
+                */
+               cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET,
+                          CC_HOST_RNG_IRQ_MASK);
+
+               irr &= ~CC_HOST_RNG_IRQ_MASK;
+
+               /* schedule execution of deferred work handler */
+               schedule_work(&drvdata->compwork);
+       }
+
+       if (irr) {
+               dev_dbg_ratelimited(dev,
+                               "IRR includes unknown cause bits (0x%08X)\n",
+                               irr);
+               /* Just warning */
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void cc_trng_startwork_handler(struct work_struct *w)
+{
+       struct cctrng_drvdata *drvdata =
+                       container_of(w, struct cctrng_drvdata, startwork);
+
+       drvdata->active_rosc = 0;
+       cc_trng_hw_trigger(drvdata);
+}
+
+
+static int cc_trng_clk_init(struct cctrng_drvdata *drvdata)
+{
+       struct clk *clk;
+       struct device *dev = &(drvdata->pdev->dev);
+       int rc = 0;
+
+       clk = devm_clk_get_optional(dev, NULL);
+       if (IS_ERR(clk)) {
+               if (PTR_ERR(clk) != -EPROBE_DEFER)
+                       dev_err(dev, "Error getting clock: %pe\n", clk);
+               return PTR_ERR(clk);
+       }
+       drvdata->clk = clk;
+
+       rc = clk_prepare_enable(drvdata->clk);
+       if (rc) {
+               dev_err(dev, "Failed to enable clock\n");
+               return rc;
+       }
+
+       return 0;
+}
+
+static void cc_trng_clk_fini(struct cctrng_drvdata *drvdata)
+{
+       clk_disable_unprepare(drvdata->clk);
+}
+
+
+static int cctrng_probe(struct platform_device *pdev)
+{
+       struct resource *req_mem_cc_regs = NULL;
+       struct cctrng_drvdata *drvdata;
+       struct device *dev = &pdev->dev;
+       int rc = 0;
+       u32 val;
+       int irq;
+
+       drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+       if (!drvdata)
+               return -ENOMEM;
+
+       drvdata->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+       if (!drvdata->rng.name)
+               return -ENOMEM;
+
+       drvdata->rng.read = cctrng_read;
+       drvdata->rng.priv = (unsigned long)drvdata;
+       drvdata->rng.quality = CC_TRNG_QUALITY;
+
+       platform_set_drvdata(pdev, drvdata);
+       drvdata->pdev = pdev;
+
+       drvdata->circ.buf = (char *)drvdata->data_buf;
+
+       /* Get device resources */
+       /* First CC registers space */
+       req_mem_cc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       /* Map registers space */
+       drvdata->cc_base = devm_ioremap_resource(dev, req_mem_cc_regs);
+       if (IS_ERR(drvdata->cc_base)) {
+               dev_err(dev, "Failed to ioremap registers");
+               return PTR_ERR(drvdata->cc_base);
+       }
+
+       dev_dbg(dev, "Got MEM resource (%s): %pR\n", req_mem_cc_regs->name,
+               req_mem_cc_regs);
+       dev_dbg(dev, "CC registers mapped from %pa to 0x%p\n",
+               &req_mem_cc_regs->start, drvdata->cc_base);
+
+       /* Then IRQ */
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "Failed getting IRQ resource\n");
+               return irq;
+       }
+
+       /* parse sampling rate from device tree */
+       rc = cc_trng_parse_sampling_ratio(drvdata);
+       if (rc) {
+               dev_err(dev, "Failed to get legal sampling ratio for rosc\n");
+               return rc;
+       }
+
+       rc = cc_trng_clk_init(drvdata);
+       if (rc) {
+               dev_err(dev, "cc_trng_clk_init failed\n");
+               return rc;
+       }
+
+       INIT_WORK(&drvdata->compwork, cc_trng_compwork_handler);
+       INIT_WORK(&drvdata->startwork, cc_trng_startwork_handler);
+       spin_lock_init(&drvdata->read_lock);
+
+       /* register the driver isr function */
+       rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata);
+       if (rc) {
+               dev_err(dev, "Could not register to interrupt %d\n", irq);
+               goto post_clk_err;
+       }
+       dev_dbg(dev, "Registered to IRQ: %d\n", irq);
+
+       /* Clear all pending interrupts */
+       val = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET);
+       dev_dbg(dev, "IRR=0x%08X\n", val);
+       cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, val);
+
+       /* unmask HOST RNG interrupt */
+       cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET,
+                  cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) &
+                  ~CC_HOST_RNG_IRQ_MASK);
+
+       /* init PM */
+       rc = cc_trng_pm_init(drvdata);
+       if (rc) {
+               dev_err(dev, "cc_trng_pm_init failed\n");
+               goto post_clk_err;
+       }
+
+       /* increment device's usage counter */
+       rc = cc_trng_pm_get(dev);
+       if (rc) {
+               dev_err(dev, "cc_trng_pm_get returned %x\n", rc);
+               goto post_pm_err;
+       }
+
+       /* set pending_hw to verify that HW won't be triggered from read */
+       atomic_set(&drvdata->pending_hw, 1);
+
+       /* registration of the hwrng device */
+       rc = hwrng_register(&drvdata->rng);
+       if (rc) {
+               dev_err(dev, "Could not register hwrng device.\n");
+               goto post_pm_err;
+       }
+
+       /* trigger HW to start generate data */
+       drvdata->active_rosc = 0;
+       cc_trng_hw_trigger(drvdata);
+
+       /* All set, we can allow auto-suspend */
+       cc_trng_pm_go(drvdata);
+
+       dev_info(dev, "ARM cctrng device initialized\n");
+
+       return 0;
+
+post_pm_err:
+       cc_trng_pm_fini(drvdata);
+
+post_clk_err:
+       cc_trng_clk_fini(drvdata);
+
+       return rc;
+}
+
+static int cctrng_remove(struct platform_device *pdev)
+{
+       struct cctrng_drvdata *drvdata = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+
+       dev_dbg(dev, "Releasing cctrng resources...\n");
+
+       hwrng_unregister(&drvdata->rng);
+
+       cc_trng_pm_fini(drvdata);
+
+       cc_trng_clk_fini(drvdata);
+
+       dev_info(dev, "ARM cctrng device terminated\n");
+
+       return 0;
+}
+
+static int __maybe_unused cctrng_suspend(struct device *dev)
+{
+       struct cctrng_drvdata *drvdata = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "set HOST_POWER_DOWN_EN\n");
+       cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET,
+                       POWER_DOWN_ENABLE);
+
+       clk_disable_unprepare(drvdata->clk);
+
+       return 0;
+}
+
+static bool cctrng_wait_for_reset_completion(struct cctrng_drvdata *drvdata)
+{
+       unsigned int val;
+       unsigned int i;
+
+       for (i = 0; i < CC_HW_RESET_LOOP_COUNT; i++) {
+               /* in cc7x3 NVM_IS_IDLE indicates that CC reset is
+                *  completed and device is fully functional
+                */
+               val = cc_ioread(drvdata, CC_NVM_IS_IDLE_REG_OFFSET);
+               if (val & BIT(CC_NVM_IS_IDLE_VALUE_BIT_SHIFT)) {
+                       /* hw indicate reset completed */
+                       return true;
+               }
+               /* allow scheduling other process on the processor */
+               schedule();
+       }
+       /* reset not completed */
+       return false;
+}
+
+static int __maybe_unused cctrng_resume(struct device *dev)
+{
+       struct cctrng_drvdata *drvdata = dev_get_drvdata(dev);
+       int rc;
+
+       dev_dbg(dev, "unset HOST_POWER_DOWN_EN\n");
+       /* Enables the device source clk */
+       rc = clk_prepare_enable(drvdata->clk);
+       if (rc) {
+               dev_err(dev, "failed getting clock back on. We're toast.\n");
+               return rc;
+       }
+
+       /* wait for Cryptocell reset completion */
+       if (!cctrng_wait_for_reset_completion(drvdata)) {
+               dev_err(dev, "Cryptocell reset not completed");
+               return -EBUSY;
+       }
+
+       /* unmask HOST RNG interrupt */
+       cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET,
+                  cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) &
+                  ~CC_HOST_RNG_IRQ_MASK);
+
+       cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET,
+                  POWER_DOWN_DISABLE);
+
+       return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(cctrng_pm, cctrng_suspend, cctrng_resume, NULL);
+
+static const struct of_device_id arm_cctrng_dt_match[] = {
+       { .compatible = "arm,cryptocell-713-trng", },
+       { .compatible = "arm,cryptocell-703-trng", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, arm_cctrng_dt_match);
+
+static struct platform_driver cctrng_driver = {
+       .driver = {
+               .name = "cctrng",
+               .of_match_table = arm_cctrng_dt_match,
+               .pm = &cctrng_pm,
+       },
+       .probe = cctrng_probe,
+       .remove = cctrng_remove,
+};
+
+static int __init cctrng_mod_init(void)
+{
+       /* Compile time assertion checks */
+       BUILD_BUG_ON(CCTRNG_DATA_BUF_WORDS < 6);
+       BUILD_BUG_ON((CCTRNG_DATA_BUF_WORDS & (CCTRNG_DATA_BUF_WORDS-1)) != 0);
+
+       return platform_driver_register(&cctrng_driver);
+}
+module_init(cctrng_mod_init);
+
+static void __exit cctrng_mod_exit(void)
+{
+       platform_driver_unregister(&cctrng_driver);
+}
+module_exit(cctrng_mod_exit);
+
+/* Module description */
+MODULE_DESCRIPTION("ARM CryptoCell TRNG Driver");
+MODULE_AUTHOR("ARM");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/cctrng.h b/drivers/char/hw_random/cctrng.h
new file mode 100644 (file)
index 0000000..1f2fde9
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */
+
+#include <linux/bitops.h>
+
+#define POWER_DOWN_ENABLE 0x01
+#define POWER_DOWN_DISABLE 0x00
+
+/* hwrng quality: bits of true entropy per 1024 bits of input */
+#define CC_TRNG_QUALITY        1024
+
+/* CryptoCell TRNG HW definitions */
+#define CC_TRNG_NUM_OF_ROSCS   4
+/* The number of words generated in the entropy holding register (EHR)
+ * 6 words (192 bit) according to HW implementation
+ */
+#define CC_TRNG_EHR_IN_WORDS   6
+#define CC_TRNG_EHR_IN_BITS    (CC_TRNG_EHR_IN_WORDS * BITS_PER_TYPE(u32))
+
+#define CC_HOST_RNG_IRQ_MASK BIT(CC_HOST_RGF_IRR_RNG_INT_BIT_SHIFT)
+
+/* RNG interrupt mask */
+#define CC_RNG_INT_MASK (BIT(CC_RNG_IMR_EHR_VALID_INT_MASK_BIT_SHIFT) | \
+                        BIT(CC_RNG_IMR_AUTOCORR_ERR_INT_MASK_BIT_SHIFT) | \
+                        BIT(CC_RNG_IMR_CRNGT_ERR_INT_MASK_BIT_SHIFT) | \
+                        BIT(CC_RNG_IMR_VN_ERR_INT_MASK_BIT_SHIFT) | \
+                        BIT(CC_RNG_IMR_WATCHDOG_INT_MASK_BIT_SHIFT))
+
+// --------------------------------------
+// BLOCK: RNG
+// --------------------------------------
+#define CC_RNG_IMR_REG_OFFSET  0x0100UL
+#define CC_RNG_IMR_EHR_VALID_INT_MASK_BIT_SHIFT        0x0UL
+#define CC_RNG_IMR_AUTOCORR_ERR_INT_MASK_BIT_SHIFT     0x1UL
+#define CC_RNG_IMR_CRNGT_ERR_INT_MASK_BIT_SHIFT        0x2UL
+#define CC_RNG_IMR_VN_ERR_INT_MASK_BIT_SHIFT   0x3UL
+#define CC_RNG_IMR_WATCHDOG_INT_MASK_BIT_SHIFT 0x4UL
+#define CC_RNG_ISR_REG_OFFSET  0x0104UL
+#define CC_RNG_ISR_EHR_VALID_BIT_SHIFT 0x0UL
+#define CC_RNG_ISR_EHR_VALID_BIT_SIZE  0x1UL
+#define CC_RNG_ISR_AUTOCORR_ERR_BIT_SHIFT      0x1UL
+#define CC_RNG_ISR_AUTOCORR_ERR_BIT_SIZE       0x1UL
+#define CC_RNG_ISR_CRNGT_ERR_BIT_SHIFT 0x2UL
+#define CC_RNG_ISR_CRNGT_ERR_BIT_SIZE  0x1UL
+#define CC_RNG_ISR_WATCHDOG_BIT_SHIFT  0x4UL
+#define CC_RNG_ISR_WATCHDOG_BIT_SIZE   0x1UL
+#define CC_RNG_ICR_REG_OFFSET  0x0108UL
+#define CC_TRNG_CONFIG_REG_OFFSET      0x010CUL
+#define CC_EHR_DATA_0_REG_OFFSET       0x0114UL
+#define CC_RND_SOURCE_ENABLE_REG_OFFSET        0x012CUL
+#define CC_SAMPLE_CNT1_REG_OFFSET      0x0130UL
+#define CC_TRNG_DEBUG_CONTROL_REG_OFFSET       0x0138UL
+#define CC_RNG_SW_RESET_REG_OFFSET     0x0140UL
+#define CC_RNG_CLK_ENABLE_REG_OFFSET   0x01C4UL
+#define CC_RNG_DMA_ENABLE_REG_OFFSET   0x01C8UL
+#define CC_RNG_WATCHDOG_VAL_REG_OFFSET 0x01D8UL
+// --------------------------------------
+// BLOCK: SEC_HOST_RGF
+// --------------------------------------
+#define CC_HOST_RGF_IRR_REG_OFFSET     0x0A00UL
+#define CC_HOST_RGF_IRR_RNG_INT_BIT_SHIFT      0xAUL
+#define CC_HOST_RGF_IMR_REG_OFFSET     0x0A04UL
+#define CC_HOST_RGF_ICR_REG_OFFSET     0x0A08UL
+
+#define CC_HOST_POWER_DOWN_EN_REG_OFFSET       0x0A78UL
+
+// --------------------------------------
+// BLOCK: NVM
+// --------------------------------------
+#define CC_NVM_IS_IDLE_REG_OFFSET      0x0F10UL
+#define CC_NVM_IS_IDLE_VALUE_BIT_SHIFT 0x0UL
+#define CC_NVM_IS_IDLE_VALUE_BIT_SIZE  0x1UL
index 65952393e1bb85a3e237b970f5ba32544fcb6722..7290c603fcb8ee6aa0ea96a6522124f7b29359cc 100644 (file)
@@ -392,11 +392,8 @@ static int of_get_omap_rng_device_details(struct omap_rng_dev *priv,
        if (of_device_is_compatible(dev->of_node, "ti,omap4-rng") ||
            of_device_is_compatible(dev->of_node, "inside-secure,safexcel-eip76")) {
                irq = platform_get_irq(pdev, 0);
-               if (irq < 0) {
-                       dev_err(dev, "%s: error getting IRQ resource - %d\n",
-                               __func__, irq);
+               if (irq < 0)
                        return irq;
-               }
 
                err = devm_request_irq(dev, irq, omap4_rng_irq,
                                       IRQF_TRIGGER_NONE, dev_name(dev), priv);
index ddfbabaa5f8f61517eeaa8e187259ab1111410fd..49b2e02537ddb729395be08d83fbe95a3e50db17 100644 (file)
@@ -226,7 +226,7 @@ static int optee_rng_probe(struct device *dev)
                return -ENODEV;
 
        /* Open session with hwrng Trusted App */
-       memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
+       export_uuid(sess_arg.uuid, &rng_device->id.uuid);
        sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
        sess_arg.num_params = 0;
 
index d7516a446987b6095bbf8cca42349062029535f8..008e6db9ce010b3e219178f6a05ccfff70b3e753 100644 (file)
@@ -328,10 +328,8 @@ static int xgene_rng_probe(struct platform_device *pdev)
                return PTR_ERR(ctx->csr_base);
 
        rc = platform_get_irq(pdev, 0);
-       if (rc < 0) {
-               dev_err(&pdev->dev, "No IRQ resource\n");
+       if (rc < 0)
                return rc;
-       }
        ctx->irq = rc;
 
        dev_dbg(&pdev->dev, "APM X-Gene RNG BASE %p ALARM IRQ %d",
index 7dc2c3ec40516775c40d014bbc3e42c4819e1960..07847d9a459afc709d62db19a121fd6cef0edc93 100644 (file)
@@ -14,7 +14,7 @@ menuconfig IPMI_HANDLER
          IPMI is a standard for managing sensors (temperature,
          voltage, etc.) in a system.
 
-         See <file:Documentation/IPMI.txt> for more details on the driver.
+         See <file:Documentation/driver-api/ipmi.rst> for more details on the driver.
 
          If unsure, say N.
 
index 42a925f8cf69e6923dced09f2fb392edd523446b..4fbb4e18bae21310749696db8e85001520b3aee8 100644 (file)
@@ -18,7 +18,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp);
 
 module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
 MODULE_PARM_DESC(hotmod, "Add and remove interfaces.  See"
-                " Documentation/IPMI.txt in the kernel sources for the"
+                " Documentation/driver-api/ipmi.rst in the kernel sources for the"
                 " gory details.");
 
 /*
index c7cc8538b84aec726b3483197636e4e38e3ff4ae..77b8d551ae7fe83b3058a40db7b62703a0017bbd 100644 (file)
@@ -968,7 +968,7 @@ static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
  * that are not BT and do not have interrupts.  It starts spinning
  * when an operation is complete or until max_busy tells it to stop
  * (if that is enabled).  See the paragraph on kimid_max_busy_us in
- * Documentation/IPMI.txt for details.
+ * Documentation/driver-api/ipmi.rst for details.
  */
 static int ipmi_thread(void *data)
 {
index 4667844eee69d35d12a15976fd9ca630e2f75193..8206412d25ba7ee326c929e86bc0c97850420ec7 100644 (file)
@@ -232,8 +232,6 @@ static ssize_t nvram_misc_read(struct file *file, char __user *buf,
        ssize_t ret;
 
 
-       if (!access_ok(buf, count))
-               return -EFAULT;
        if (*ppos >= nvram_size)
                return 0;
 
@@ -264,8 +262,6 @@ static ssize_t nvram_misc_write(struct file *file, const char __user *buf,
        char *tmp;
        ssize_t ret;
 
-       if (!access_ok(buf, count))
-               return -EFAULT;
        if (*ppos >= nvram_size)
                return 0;
 
index 4edb4174a1e287d11b65e0d2b67afe6d728ed2e3..89681f07bc787297fa1aae10afc7f500aea8a4bd 100644 (file)
@@ -1404,7 +1404,6 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        unsigned int iobase = dev->p_dev->resource[0]->start;
        struct inode *inode = file_inode(filp);
        struct pcmcia_device *link;
-       int size;
        int rc;
        void __user *argp = (void __user *)arg;
 #ifdef CM4000_DEBUG
@@ -1441,19 +1440,6 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                DEBUGP(4, dev, "iocnr mismatch\n");
                goto out;
        }
-       size = _IOC_SIZE(cmd);
-       rc = -EFAULT;
-       DEBUGP(4, dev, "iocdir=%.4x iocr=%.4x iocw=%.4x iocsize=%d cmd=%.4x\n",
-             _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd);
-
-       if (_IOC_DIR(cmd) & _IOC_READ) {
-               if (!access_ok(argp, size))
-                       goto out;
-       }
-       if (_IOC_DIR(cmd) & _IOC_WRITE) {
-               if (!access_ok(argp, size))
-                       goto out;
-       }
        rc = 0;
 
        switch (cmd) {
index 0d10e31fd342f52416a41953dcad209e77bdd8e6..cae02b2a871c32392652f39048d8a256a6fe725e 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/kthread.h>
 #include <linux/percpu.h>
-#include <linux/cryptohash.h>
 #include <linux/fips.h>
 #include <linux/ptrace.h>
 #include <linux/workqueue.h>
 #include <linux/completion.h>
 #include <linux/uuid.h>
 #include <crypto/chacha.h>
+#include <crypto/sha.h>
 
 #include <asm/processor.h>
 #include <linux/uaccess.h>
@@ -1397,14 +1397,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
                __u32 w[5];
                unsigned long l[LONGS(20)];
        } hash;
-       __u32 workspace[SHA_WORKSPACE_WORDS];
+       __u32 workspace[SHA1_WORKSPACE_WORDS];
        unsigned long flags;
 
        /*
         * If we have an architectural hardware random number
         * generator, use it for SHA's initial vector
         */
-       sha_init(hash.w);
+       sha1_init(hash.w);
        for (i = 0; i < LONGS(20); i++) {
                unsigned long v;
                if (!arch_get_random_long(&v))
@@ -1415,7 +1415,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
        /* Generate a hash across the pool, 16 words (512 bits) at a time */
        spin_lock_irqsave(&r->lock, flags);
        for (i = 0; i < r->poolinfo->poolwords; i += 16)
-               sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
+               sha1_transform(hash.w, (__u8 *)(r->pool + i), workspace);
 
        /*
         * We mix the hash back into the pool to prevent backtracking
index e741b115752588e1f8dc3d56b20ffb1309900c64..37a05800980c85b4ce446eea3ef848b062ca3bcd 100644 (file)
@@ -51,8 +51,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
        int i;
 
        event_header = addr;
-       size = sizeof(struct tcg_pcr_event) - sizeof(event_header->event)
-               + event_header->event_size;
+       size = struct_size(event_header, event, event_header->event_size);
 
        if (*pos == 0) {
                if (addr + size < limit) {
@@ -98,8 +97,8 @@ static void *tpm2_bios_measurements_next(struct seq_file *m, void *v,
        event_header = log->bios_event_log;
 
        if (v == SEQ_START_TOKEN) {
-               event_size = sizeof(struct tcg_pcr_event) -
-                       sizeof(event_header->event) + event_header->event_size;
+               event_size = struct_size(event_header, event,
+                                        event_header->event_size);
                marker = event_header;
        } else {
                event = v;
@@ -136,9 +135,8 @@ static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v)
        size_t size;
 
        if (v == SEQ_START_TOKEN) {
-               size = sizeof(struct tcg_pcr_event) -
-                       sizeof(event_header->event) + event_header->event_size;
-
+               size = struct_size(event_header, event,
+                                  event_header->event_size);
                temp_ptr = event_header;
 
                if (size > 0)
index 22bf553ccf9df35e61c411707a78373f4ea8aa38..2491a2cb54a22c1bf9c1e24183b95f7396b6ba37 100644 (file)
@@ -241,7 +241,7 @@ static int ftpm_tee_probe(struct platform_device *pdev)
 
        /* Open a session with fTPM TA */
        memset(&sess_arg, 0, sizeof(sess_arg));
-       memcpy(sess_arg.uuid, ftpm_ta_uuid.b, TEE_IOCTL_UUID_LEN);
+       export_uuid(sess_arg.uuid, &ftpm_ta_uuid);
        sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
        sess_arg.num_params = 0;
 
index d5946f7486d6c21fd904b46ee9434f891a3c5dcb..374afcab89af5978c076826c21699ab3d58b0b14 100644 (file)
@@ -95,6 +95,7 @@ struct clockgen {
 };
 
 static struct clockgen clockgen;
+static bool add_cpufreq_dev __initdata;
 
 static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg)
 {
@@ -1019,7 +1020,7 @@ static void __init create_muxes(struct clockgen *cg)
        }
 }
 
-static void __init clockgen_init(struct device_node *np);
+static void __init _clockgen_init(struct device_node *np, bool legacy);
 
 /*
  * Legacy nodes may get probed before the parent clockgen node.
@@ -1030,7 +1031,7 @@ static void __init clockgen_init(struct device_node *np);
 static void __init legacy_init_clockgen(struct device_node *np)
 {
        if (!clockgen.node)
-               clockgen_init(of_get_parent(np));
+               _clockgen_init(of_get_parent(np), true);
 }
 
 /* Legacy node */
@@ -1447,7 +1448,7 @@ static bool __init has_erratum_a4510(void)
 }
 #endif
 
-static void __init clockgen_init(struct device_node *np)
+static void __init _clockgen_init(struct device_node *np, bool legacy)
 {
        int i, ret;
        bool is_old_ls1021a = false;
@@ -1516,12 +1517,35 @@ static void __init clockgen_init(struct device_node *np)
                       __func__, np, ret);
        }
 
+       /* Don't create cpufreq device for legacy clockgen blocks */
+       add_cpufreq_dev = !legacy;
+
        return;
 err:
        iounmap(clockgen.regs);
        clockgen.regs = NULL;
 }
 
+static void __init clockgen_init(struct device_node *np)
+{
+       _clockgen_init(np, false);
+}
+
+static int __init clockgen_cpufreq_init(void)
+{
+       struct platform_device *pdev;
+
+       if (add_cpufreq_dev) {
+               pdev = platform_device_register_simple("qoriq-cpufreq", -1,
+                               NULL, 0);
+               if (IS_ERR(pdev))
+                       pr_err("Couldn't register qoriq-cpufreq err=%ld\n",
+                               PTR_ERR(pdev));
+       }
+       return 0;
+}
+device_initcall(clockgen_cpufreq_init);
+
 CLK_OF_DECLARE(qoriq_clockgen_1, "fsl,qoriq-clockgen-1.0", clockgen_init);
 CLK_OF_DECLARE(qoriq_clockgen_2, "fsl,qoriq-clockgen-2.0", clockgen_init);
 CLK_OF_DECLARE(qoriq_clockgen_b4420, "fsl,b4420-clockgen", clockgen_init);
index 2dfb30b963c47c7ed3c35b5cc8e17b6a379252c8..407f6919604c7d124850d485337c4cb202423264 100644 (file)
@@ -114,7 +114,11 @@ static int clk_pm_runtime_get(struct clk_core *core)
                return 0;
 
        ret = pm_runtime_get_sync(core->dev);
-       return ret < 0 ? ret : 0;
+       if (ret < 0) {
+               pm_runtime_put_noidle(core->dev);
+               return ret;
+       }
+       return 0;
 }
 
 static void clk_pm_runtime_put(struct clk_core *core)
index 11ec6f46646782fe6be893757d9e28e63feabdc6..abb121f8de527707c906b5a8b6bec621c887b110 100644 (file)
@@ -377,6 +377,7 @@ config SM_GCC_8150
 
 config SM_GCC_8250
        tristate "SM8250 Global Clock Controller"
+       select QCOM_GDSC
        help
          Support for the global clock controller on SM8250 devices.
          Say Y if you want to use peripheral devices such as UART,
index ef98fdc51755c5cc46849bcf7aaafdfc5d362ce1..732bc7c937e6d80c4cded5cf17b0e8ec16c7b48d 100644 (file)
@@ -76,8 +76,7 @@ static struct clk_alpha_pll_postdiv gpll0_out_even = {
        .clkr.hw.init = &(struct clk_init_data){
                .name = "gpll0_out_even",
                .parent_data = &(const struct clk_parent_data){
-                       .fw_name = "bi_tcxo",
-                       .name = "bi_tcxo",
+                       .hw = &gpll0.clkr.hw,
                },
                .num_parents = 1,
                .ops = &clk_trion_pll_postdiv_ops,
index d58ce664da8435050aa500326ca1ca07ba159f35..646ad385e490432441514bfc818950822ce44631 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/pid_namespace.h>
 
 #include <linux/cn_proc.h>
+#include <linux/local_lock.h>
 
 /*
  * Size of a cn_msg followed by a proc_event structure.  Since the
@@ -38,25 +39,31 @@ static inline struct cn_msg *buffer_to_cn_msg(__u8 *buffer)
 static atomic_t proc_event_num_listeners = ATOMIC_INIT(0);
 static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
 
-/* proc_event_counts is used as the sequence number of the netlink message */
-static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 };
+/* local_event.count is used as the sequence number of the netlink message */
+struct local_event {
+       local_lock_t lock;
+       __u32 count;
+};
+static DEFINE_PER_CPU(struct local_event, local_event) = {
+       .lock = INIT_LOCAL_LOCK(lock),
+};
 
 static inline void send_msg(struct cn_msg *msg)
 {
-       preempt_disable();
+       local_lock(&local_event.lock);
 
-       msg->seq = __this_cpu_inc_return(proc_event_counts) - 1;
+       msg->seq = __this_cpu_inc_return(local_event.count) - 1;
        ((struct proc_event *)msg->data)->cpu = smp_processor_id();
 
        /*
-        * Preemption remains disabled during send to ensure the messages are
-        * ordered according to their sequence numbers.
+        * local_lock() disables preemption during send to ensure the messages
+        * are ordered according to their sequence numbers.
         *
         * If cn_netlink_send() fails, the data is not sent.
         */
        cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_NOWAIT);
 
-       preempt_enable();
+       local_unlock(&local_event.lock);
 }
 
 void proc_fork_connector(struct task_struct *task)
index c3e6bd59e920882268c695ef03b02d800978e963..e917501325525f168d03c18340d9134eb30ad7aa 100644 (file)
@@ -323,7 +323,8 @@ endif
 
 config QORIQ_CPUFREQ
        tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
-       depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64)
+       depends on OF && COMMON_CLK
+       depends on PPC_E500MC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
        select CLK_QORIQ
        help
          This adds the CPUFreq driver support for Freescale QorIQ SoCs
index 15c1a12315164e243050900ffcc78e311d0bf4fd..9481292981f0c9b1a81cd9a77b7c98868f48552d 100644 (file)
@@ -317,6 +317,7 @@ config ARM_TEGRA186_CPUFREQ
 config ARM_TI_CPUFREQ
        bool "Texas Instruments CPUFreq support"
        depends on ARCH_OMAP2PLUS
+       default ARCH_OMAP2PLUS
        help
          This driver enables valid OPPs on the running platform based on
          values contained within the SoC in use. Enable this in order to
index cb9db16bea6176fb825a9aa0a96ae05110d5988d..e8e20fef400b0ce5ef1aa4cd272562f61e58f0a8 100644 (file)
@@ -53,6 +53,7 @@ static const struct of_device_id whitelist[] __initconst = {
        { .compatible = "renesas,r7s72100", },
        { .compatible = "renesas,r8a73a4", },
        { .compatible = "renesas,r8a7740", },
+       { .compatible = "renesas,r8a7742", },
        { .compatible = "renesas,r8a7743", },
        { .compatible = "renesas,r8a7744", },
        { .compatible = "renesas,r8a7745", },
@@ -105,6 +106,7 @@ static const struct of_device_id blacklist[] __initconst = {
        { .compatible = "calxeda,highbank", },
        { .compatible = "calxeda,ecx-2000", },
 
+       { .compatible = "fsl,imx7ulp", },
        { .compatible = "fsl,imx7d", },
        { .compatible = "fsl,imx8mq", },
        { .compatible = "fsl,imx8mm", },
index 045f9fe157ce2cc638accc0a1a9eedc795ebf02a..d03f250f68e44346d6d2cb9e22ec8f4126d0d5fa 100644 (file)
@@ -2535,26 +2535,27 @@ EXPORT_SYMBOL_GPL(cpufreq_update_limits);
 static int cpufreq_boost_set_sw(int state)
 {
        struct cpufreq_policy *policy;
-       int ret = -EINVAL;
 
        for_each_active_policy(policy) {
+               int ret;
+
                if (!policy->freq_table)
-                       continue;
+                       return -ENXIO;
 
                ret = cpufreq_frequency_table_cpuinfo(policy,
                                                      policy->freq_table);
                if (ret) {
                        pr_err("%s: Policy frequency update failed\n",
                               __func__);
-                       break;
+                       return ret;
                }
 
                ret = freq_qos_update_request(policy->max_freq_req, policy->max);
                if (ret < 0)
-                       break;
+                       return ret;
        }
 
-       return ret;
+       return 0;
 }
 
 int cpufreq_boost_trigger_state(int state)
index de206d2745feb4aaf5618e8370c751561c8347d3..3fe9125156b443db3d93529026c91a2e2ce44ff8 100644 (file)
@@ -3,7 +3,9 @@
  * Copyright 2019 NXP
  */
 
+#include <linux/clk.h>
 #include <linux/cpu.h>
+#include <linux/cpufreq.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
+#include "cpufreq-dt.h"
+
 #define OCOTP_CFG3_SPEED_GRADE_SHIFT   8
 #define OCOTP_CFG3_SPEED_GRADE_MASK    (0x3 << 8)
 #define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK     (0xf << 8)
 #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT    5
 #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK     (0x3 << 5)
 
+#define IMX7ULP_MAX_RUN_FREQ   528000
+
 /* cpufreq-dt device registered by imx-cpufreq-dt */
 static struct platform_device *cpufreq_dt_pdev;
 static struct opp_table *cpufreq_opp_table;
+static struct device *cpu_dev;
+
+enum IMX7ULP_CPUFREQ_CLKS {
+       ARM,
+       CORE,
+       SCS_SEL,
+       HSRUN_CORE,
+       HSRUN_SCS_SEL,
+       FIRC,
+};
+
+static struct clk_bulk_data imx7ulp_clks[] = {
+       { .id = "arm" },
+       { .id = "core" },
+       { .id = "scs_sel" },
+       { .id = "hsrun_core" },
+       { .id = "hsrun_scs_sel" },
+       { .id = "firc" },
+};
+
+static unsigned int imx7ulp_get_intermediate(struct cpufreq_policy *policy,
+                                            unsigned int index)
+{
+       return clk_get_rate(imx7ulp_clks[FIRC].clk);
+}
+
+static int imx7ulp_target_intermediate(struct cpufreq_policy *policy,
+                                       unsigned int index)
+{
+       unsigned int newfreq = policy->freq_table[index].frequency;
+
+       clk_set_parent(imx7ulp_clks[SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
+       clk_set_parent(imx7ulp_clks[HSRUN_SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
+
+       if (newfreq > IMX7ULP_MAX_RUN_FREQ)
+               clk_set_parent(imx7ulp_clks[ARM].clk,
+                              imx7ulp_clks[HSRUN_CORE].clk);
+       else
+               clk_set_parent(imx7ulp_clks[ARM].clk, imx7ulp_clks[CORE].clk);
+
+       return 0;
+}
+
+static struct cpufreq_dt_platform_data imx7ulp_data = {
+       .target_intermediate = imx7ulp_target_intermediate,
+       .get_intermediate = imx7ulp_get_intermediate,
+};
 
 static int imx_cpufreq_dt_probe(struct platform_device *pdev)
 {
-       struct device *cpu_dev = get_cpu_device(0);
+       struct platform_device *dt_pdev;
        u32 cell_value, supported_hw[2];
        int speed_grade, mkt_segment;
        int ret;
 
+       cpu_dev = get_cpu_device(0);
+
        if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL))
                return -ENODEV;
 
+       if (of_machine_is_compatible("fsl,imx7ulp")) {
+               ret = clk_bulk_get(cpu_dev, ARRAY_SIZE(imx7ulp_clks),
+                                  imx7ulp_clks);
+               if (ret)
+                       return ret;
+
+               dt_pdev = platform_device_register_data(NULL, "cpufreq-dt",
+                                                       -1, &imx7ulp_data,
+                                                       sizeof(imx7ulp_data));
+               if (IS_ERR(dt_pdev)) {
+                       clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
+                       ret = PTR_ERR(dt_pdev);
+                       dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
+                       return ret;
+               }
+
+               cpufreq_dt_pdev = dt_pdev;
+
+               return 0;
+       }
+
        ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value);
        if (ret)
                return ret;
@@ -98,7 +175,10 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev)
 static int imx_cpufreq_dt_remove(struct platform_device *pdev)
 {
        platform_device_unregister(cpufreq_dt_pdev);
-       dev_pm_opp_put_supported_hw(cpufreq_opp_table);
+       if (!of_machine_is_compatible("fsl,imx7ulp"))
+               dev_pm_opp_put_supported_hw(cpufreq_opp_table);
+       else
+               clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
 
        return 0;
 }
index 4d3429b2058fcc5f16582b53c516922c263d56ae..8e23a698ce048b92fdecfb25b6eda72e5437365b 100644 (file)
@@ -2771,6 +2771,8 @@ static int __init intel_pstate_init(void)
                pr_info("Invalid MSRs\n");
                return -ENODEV;
        }
+       /* Without HWP start in the passive mode. */
+       default_driver = &intel_cpufreq;
 
 hwp_cpu_matched:
        /*
@@ -2816,7 +2818,6 @@ static int __init intel_pstate_setup(char *str)
        if (!strcmp(str, "disable")) {
                no_load = 1;
        } else if (!strcmp(str, "passive")) {
-               pr_info("Passive mode enabled\n");
                default_driver = &intel_cpufreq;
                no_hwp = 1;
        }
index a1b8238872a21d012d121d453fcf7250966fdfad..d06b37822c3dff1add1d505474efd9ecb8422f95 100644 (file)
@@ -277,7 +277,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev)
        if (!np)
                return -ENOENT;
 
-       ret = of_device_is_compatible(np, "operating-points-v2-qcom-cpu");
+       ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
        if (!ret) {
                of_node_put(np);
                return -ENOENT;
index 8e436dc75c8bc84b4cd8011f6062671f3a8ca3a5..6b6b20da2bcfc83846d37a3524de5c4d3f7598f2 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
+#include <linux/platform_device.h>
 
 /**
  * struct cpu_data
@@ -29,12 +30,6 @@ struct cpu_data {
        struct cpufreq_frequency_table *table;
 };
 
-/*
- * Don't use cpufreq on this SoC -- used when the SoC would have otherwise
- * matched a more generic compatible.
- */
-#define SOC_BLACKLIST          1
-
 /**
  * struct soc_data - SoC specific data
  * @flags: SOC_xxx
@@ -264,64 +259,51 @@ static struct cpufreq_driver qoriq_cpufreq_driver = {
        .attr           = cpufreq_generic_attr,
 };
 
-static const struct soc_data blacklist = {
-       .flags = SOC_BLACKLIST,
-};
-
-static const struct of_device_id node_matches[] __initconst = {
+static const struct of_device_id qoriq_cpufreq_blacklist[] = {
        /* e6500 cannot use cpufreq due to erratum A-008083 */
-       { .compatible = "fsl,b4420-clockgen", &blacklist },
-       { .compatible = "fsl,b4860-clockgen", &blacklist },
-       { .compatible = "fsl,t2080-clockgen", &blacklist },
-       { .compatible = "fsl,t4240-clockgen", &blacklist },
-
-       { .compatible = "fsl,ls1012a-clockgen", },
-       { .compatible = "fsl,ls1021a-clockgen", },
-       { .compatible = "fsl,ls1028a-clockgen", },
-       { .compatible = "fsl,ls1043a-clockgen", },
-       { .compatible = "fsl,ls1046a-clockgen", },
-       { .compatible = "fsl,ls1088a-clockgen", },
-       { .compatible = "fsl,ls2080a-clockgen", },
-       { .compatible = "fsl,lx2160a-clockgen", },
-       { .compatible = "fsl,p4080-clockgen", },
-       { .compatible = "fsl,qoriq-clockgen-1.0", },
-       { .compatible = "fsl,qoriq-clockgen-2.0", },
+       { .compatible = "fsl,b4420-clockgen", },
+       { .compatible = "fsl,b4860-clockgen", },
+       { .compatible = "fsl,t2080-clockgen", },
+       { .compatible = "fsl,t4240-clockgen", },
        {}
 };
 
-static int __init qoriq_cpufreq_init(void)
+static int qoriq_cpufreq_probe(struct platform_device *pdev)
 {
        int ret;
-       struct device_node  *np;
-       const struct of_device_id *match;
-       const struct soc_data *data;
-
-       np = of_find_matching_node(NULL, node_matches);
-       if (!np)
-               return -ENODEV;
-
-       match = of_match_node(node_matches, np);
-       data = match->data;
-
-       of_node_put(np);
+       struct device_node *np;
 
-       if (data && data->flags & SOC_BLACKLIST)
+       np = of_find_matching_node(NULL, qoriq_cpufreq_blacklist);
+       if (np) {
+               dev_info(&pdev->dev, "Disabling due to erratum A-008083");
                return -ENODEV;
+       }
 
        ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
-       if (!ret)
-               pr_info("Freescale QorIQ CPU frequency scaling driver\n");
+       if (ret)
+               return ret;
 
-       return ret;
+       dev_info(&pdev->dev, "Freescale QorIQ CPU frequency scaling driver\n");
+       return 0;
 }
-module_init(qoriq_cpufreq_init);
 
-static void __exit qoriq_cpufreq_exit(void)
+static int qoriq_cpufreq_remove(struct platform_device *pdev)
 {
        cpufreq_unregister_driver(&qoriq_cpufreq_driver);
+
+       return 0;
 }
-module_exit(qoriq_cpufreq_exit);
 
+static struct platform_driver qoriq_cpufreq_platform_driver = {
+       .driver = {
+               .name = "qoriq-cpufreq",
+       },
+       .probe = qoriq_cpufreq_probe,
+       .remove = qoriq_cpufreq_remove,
+};
+module_platform_driver(qoriq_cpufreq_platform_driver);
+
+MODULE_ALIAS("platform:qoriq-cpufreq");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
 MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");
index 99a2d72ac02b5c87342c16cad1a37eb96467ebcf..51a7e89085c0bdef5fa23451e55143847a786467 100644 (file)
@@ -94,3 +94,16 @@ config ARM_TEGRA_CPUIDLE
        select ARM_CPU_SUSPEND
        help
          Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs.
+
+config ARM_QCOM_SPM_CPUIDLE
+       bool "CPU Idle Driver for Qualcomm Subsystem Power Manager (SPM)"
+       depends on (ARCH_QCOM || COMPILE_TEST) && !ARM64
+       select ARM_CPU_SUSPEND
+       select CPU_IDLE_MULTIPLE_DRIVERS
+       select DT_IDLE_STATES
+       select QCOM_SCM
+       help
+         Select this to enable cpuidle for Qualcomm processors.
+         The Subsystem Power Manager (SPM) controls low power modes for the
+         CPU and L2 cores. It interface with various system drivers to put
+         the cores in low power modes.
index 55a464f6a78b60a03dde5a026b56bce8fd208717..f07800cbb43f04ab6b2d007b69ed6b9325154e0e 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_ARM_PSCI_CPUIDLE)                += cpuidle_psci.o
 cpuidle_psci-y                         := cpuidle-psci.o
 cpuidle_psci-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpuidle-psci-domain.o
 obj-$(CONFIG_ARM_TEGRA_CPUIDLE)                += cpuidle-tegra.o
+obj-$(CONFIG_ARM_QCOM_SPM_CPUIDLE)     += cpuidle-qcom-spm.o
 
 ###############################################################################
 # MIPS drivers
index bae9140a65a557292d26642786e619d1e3c9084e..d0fb585073c69221d567ebcfa53f41642dbfefcc 100644 (file)
@@ -58,6 +58,10 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
        u32 state;
        int ret;
 
+       ret = cpu_pm_enter();
+       if (ret)
+               return -1;
+
        /* Do runtime PM to manage a hierarchical CPU toplogy. */
        pm_runtime_put_sync_suspend(pd_dev);
 
@@ -65,10 +69,12 @@ static int psci_enter_domain_idle_state(struct cpuidle_device *dev,
        if (!state)
                state = states[idx];
 
-       ret = psci_enter_state(idx, state);
+       ret = psci_cpu_suspend_enter(state) ? -1 : idx;
 
        pm_runtime_get_sync(pd_dev);
 
+       cpu_pm_exit();
+
        /* Clear the domain state to start fresh when back from idle. */
        psci_set_domain_state(0);
        return ret;
similarity index 75%
rename from drivers/soc/qcom/spm.c
rename to drivers/cpuidle/cpuidle-qcom-spm.c
index 8e10e02c6aa5354dc07816dadb0708691e909687..adf91a6e4d7dc9c7caaa6f7021754d2c416eeaf9 100644 (file)
 #include <linux/cpu_pm.h>
 #include <linux/qcom_scm.h>
 
-#include <asm/cpuidle.h>
 #include <asm/proc-fns.h>
 #include <asm/suspend.h>
 
+#include "dt_idle_states.h"
+
 #define MAX_PMIC_DATA          2
 #define MAX_SEQ_DATA           64
 #define SPM_CTL_INDEX          0x7f
@@ -62,6 +63,7 @@ struct spm_reg_data {
 };
 
 struct spm_driver_data {
+       struct cpuidle_driver cpuidle_driver;
        void __iomem *reg_base;
        const struct spm_reg_data *reg_data;
 };
@@ -107,11 +109,6 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
        .start_index[PM_SLEEP_MODE_SPC] = 2,
 };
 
-static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv);
-
-typedef int (*idle_fn)(void);
-static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops);
-
 static inline void spm_register_write(struct spm_driver_data *drv,
                                        enum spm_reg reg, u32 val)
 {
@@ -172,10 +169,9 @@ static int qcom_pm_collapse(unsigned long int unused)
        return -1;
 }
 
-static int qcom_cpu_spc(void)
+static int qcom_cpu_spc(struct spm_driver_data *drv)
 {
        int ret;
-       struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv);
 
        spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC);
        ret = cpu_suspend(0, qcom_pm_collapse);
@@ -190,94 +186,49 @@ static int qcom_cpu_spc(void)
        return ret;
 }
 
-static int qcom_idle_enter(unsigned long index)
+static int spm_enter_idle_state(struct cpuidle_device *dev,
+                               struct cpuidle_driver *drv, int idx)
 {
-       return __this_cpu_read(qcom_idle_ops)[index]();
+       struct spm_driver_data *data = container_of(drv, struct spm_driver_data,
+                                                   cpuidle_driver);
+
+       return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data);
 }
 
-static const struct of_device_id qcom_idle_state_match[] __initconst = {
-       { .compatible = "qcom,idle-state-spc", .data = qcom_cpu_spc },
+static struct cpuidle_driver qcom_spm_idle_driver = {
+       .name = "qcom_spm",
+       .owner = THIS_MODULE,
+       .states[0] = {
+               .enter                  = spm_enter_idle_state,
+               .exit_latency           = 1,
+               .target_residency       = 1,
+               .power_usage            = UINT_MAX,
+               .name                   = "WFI",
+               .desc                   = "ARM WFI",
+       }
+};
+
+static const struct of_device_id qcom_idle_state_match[] = {
+       { .compatible = "qcom,idle-state-spc", .data = spm_enter_idle_state },
        { },
 };
 
-static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu)
+static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu)
 {
-       const struct of_device_id *match_id;
-       struct device_node *state_node;
-       int i;
-       int state_count = 1;
-       idle_fn idle_fns[CPUIDLE_STATE_MAX];
-       idle_fn *fns;
-       cpumask_t mask;
-       bool use_scm_power_down = false;
-
-       if (!qcom_scm_is_available())
-               return -EPROBE_DEFER;
-
-       for (i = 0; ; i++) {
-               state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
-               if (!state_node)
-                       break;
-
-               if (!of_device_is_available(state_node))
-                       continue;
-
-               if (i == CPUIDLE_STATE_MAX) {
-                       pr_warn("%s: cpuidle states reached max possible\n",
-                                       __func__);
-                       break;
-               }
-
-               match_id = of_match_node(qcom_idle_state_match, state_node);
-               if (!match_id)
-                       return -ENODEV;
-
-               idle_fns[state_count] = match_id->data;
-
-               /* Check if any of the states allow power down */
-               if (match_id->data == qcom_cpu_spc)
-                       use_scm_power_down = true;
-
-               state_count++;
-       }
-
-       if (state_count == 1)
-               goto check_spm;
-
-       fns = devm_kcalloc(get_cpu_device(cpu), state_count, sizeof(*fns),
-                       GFP_KERNEL);
-       if (!fns)
-               return -ENOMEM;
-
-       for (i = 1; i < state_count; i++)
-               fns[i] = idle_fns[i];
+       int ret;
 
-       if (use_scm_power_down) {
-               /* We have atleast one power down mode */
-               cpumask_clear(&mask);
-               cpumask_set_cpu(cpu, &mask);
-               qcom_scm_set_warm_boot_addr(cpu_resume_arm, &mask);
-       }
+       memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv));
+       drv->cpumask = (struct cpumask *)cpumask_of(cpu);
 
-       per_cpu(qcom_idle_ops, cpu) = fns;
+       /* Parse idle states from device tree */
+       ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1);
+       if (ret <= 0)
+               return ret ? : -ENODEV;
 
-       /*
-        * SPM probe for the cpu should have happened by now, if the
-        * SPM device does not exist, return -ENXIO to indicate that the
-        * cpu does not support idle states.
-        */
-check_spm:
-       return per_cpu(cpu_spm_drv, cpu) ? 0 : -ENXIO;
+       /* We have atleast one power down mode */
+       return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask);
 }
 
-static const struct cpuidle_ops qcom_cpuidle_ops __initconst = {
-       .suspend = qcom_idle_enter,
-       .init = qcom_cpuidle_init,
-};
-
-CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v1, "qcom,kpss-acc-v1", &qcom_cpuidle_ops);
-CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v2, "qcom,kpss-acc-v2", &qcom_cpuidle_ops);
-
 static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
                int *spm_cpu)
 {
@@ -323,11 +274,15 @@ static int spm_dev_probe(struct platform_device *pdev)
        struct resource *res;
        const struct of_device_id *match_id;
        void __iomem *addr;
-       int cpu;
+       int cpu, ret;
+
+       if (!qcom_scm_is_available())
+               return -EPROBE_DEFER;
 
        drv = spm_get_drv(pdev, &cpu);
        if (!drv)
                return -EINVAL;
+       platform_set_drvdata(pdev, drv);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
@@ -340,6 +295,10 @@ static int spm_dev_probe(struct platform_device *pdev)
 
        drv->reg_data = match_id->data;
 
+       ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu);
+       if (ret)
+               return ret;
+
        /* Write the SPM sequences first.. */
        addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
        __iowrite32_copy(addr, drv->reg_data->seq,
@@ -362,13 +321,20 @@ static int spm_dev_probe(struct platform_device *pdev)
        /* Set up Standby as the default low power mode */
        spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
 
-       per_cpu(cpu_spm_drv, cpu) = drv;
+       return cpuidle_register(&drv->cpuidle_driver, NULL);
+}
+
+static int spm_dev_remove(struct platform_device *pdev)
+{
+       struct spm_driver_data *drv = platform_get_drvdata(pdev);
 
+       cpuidle_unregister(&drv->cpuidle_driver);
        return 0;
 }
 
 static struct platform_driver spm_driver = {
        .probe = spm_dev_probe,
+       .remove = spm_dev_remove,
        .driver = {
                .name = "saw",
                .of_match_table = spm_match_table,
index cdeedbf02646da3b060e271b83d1ff7d57e5a6ed..091d1caceb4174cd411cc78b42752fe0c4942fb0 100644 (file)
 
 #include "cpuidle.h"
 
-static unsigned int sysfs_switch;
-static int __init cpuidle_sysfs_setup(char *unused)
-{
-       sysfs_switch = 1;
-       return 1;
-}
-__setup("cpuidle_sysfs_switch", cpuidle_sysfs_setup);
-
 static ssize_t show_available_governors(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -35,10 +27,10 @@ static ssize_t show_available_governors(struct device *dev,
 
        mutex_lock(&cpuidle_lock);
        list_for_each_entry(tmp, &cpuidle_governors, governor_list) {
-               if (i >= (ssize_t) ((PAGE_SIZE/sizeof(char)) -
-                                   CPUIDLE_NAME_LEN - 2))
+               if (i >= (ssize_t) (PAGE_SIZE - (CPUIDLE_NAME_LEN + 2)))
                        goto out;
-               i += scnprintf(&buf[i], CPUIDLE_NAME_LEN, "%s ", tmp->name);
+
+               i += scnprintf(&buf[i], CPUIDLE_NAME_LEN + 1, "%s ", tmp->name);
        }
 
 out:
@@ -85,58 +77,43 @@ static ssize_t store_current_governor(struct device *dev,
                                      struct device_attribute *attr,
                                      const char *buf, size_t count)
 {
-       char gov_name[CPUIDLE_NAME_LEN];
-       int ret = -EINVAL;
-       size_t len = count;
+       char gov_name[CPUIDLE_NAME_LEN + 1];
+       int ret;
        struct cpuidle_governor *gov;
 
-       if (!len || len >= sizeof(gov_name))
+       ret = sscanf(buf, "%" __stringify(CPUIDLE_NAME_LEN) "s", gov_name);
+       if (ret != 1)
                return -EINVAL;
 
-       memcpy(gov_name, buf, len);
-       gov_name[len] = '\0';
-       if (gov_name[len - 1] == '\n')
-               gov_name[--len] = '\0';
-
        mutex_lock(&cpuidle_lock);
-
+       ret = -EINVAL;
        list_for_each_entry(gov, &cpuidle_governors, governor_list) {
-               if (strlen(gov->name) == len && !strcmp(gov->name, gov_name)) {
+               if (!strncmp(gov->name, gov_name, CPUIDLE_NAME_LEN)) {
                        ret = cpuidle_switch_governor(gov);
                        break;
                }
        }
-
        mutex_unlock(&cpuidle_lock);
 
-       if (ret)
-               return ret;
-       else
-               return count;
+       return ret ? ret : count;
 }
 
-static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);
-static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);
-
-static struct attribute *cpuidle_default_attrs[] = {
-       &dev_attr_current_driver.attr,
-       &dev_attr_current_governor_ro.attr,
-       NULL
-};
-
 static DEVICE_ATTR(available_governors, 0444, show_available_governors, NULL);
+static DEVICE_ATTR(current_driver, 0444, show_current_driver, NULL);
 static DEVICE_ATTR(current_governor, 0644, show_current_governor,
-                  store_current_governor);
+                                  store_current_governor);
+static DEVICE_ATTR(current_governor_ro, 0444, show_current_governor, NULL);
 
-static struct attribute *cpuidle_switch_attrs[] = {
+static struct attribute *cpuidle_attrs[] = {
        &dev_attr_available_governors.attr,
        &dev_attr_current_driver.attr,
        &dev_attr_current_governor.attr,
+       &dev_attr_current_governor_ro.attr,
        NULL
 };
 
 static struct attribute_group cpuidle_attr_group = {
-       .attrs = cpuidle_default_attrs,
+       .attrs = cpuidle_attrs,
        .name = "cpuidle",
 };
 
@@ -146,9 +123,6 @@ static struct attribute_group cpuidle_attr_group = {
  */
 int cpuidle_add_interface(struct device *dev)
 {
-       if (sysfs_switch)
-               cpuidle_attr_group.attrs = cpuidle_switch_attrs;
-
        return sysfs_create_group(&dev->kobj, &cpuidle_attr_group);
 }
 
@@ -167,11 +141,6 @@ struct cpuidle_attr {
        ssize_t (*store)(struct cpuidle_device *, const char *, size_t count);
 };
 
-#define define_one_ro(_name, show) \
-       static struct cpuidle_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
-#define define_one_rw(_name, show, store) \
-       static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store)
-
 #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr)
 
 struct cpuidle_device_kobj {
@@ -431,12 +400,12 @@ static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *k
 #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
 
 static ssize_t cpuidle_state_show(struct kobject *kobj, struct attribute *attr,
-                                 char * buf)
+                                 char *buf)
 {
        int ret = -EIO;
        struct cpuidle_state *state = kobj_to_state(kobj);
        struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
-       struct cpuidle_state_attr * cattr = attr_to_stateattr(attr);
+       struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
 
        if (cattr->show)
                ret = cattr->show(state, state_usage, buf);
@@ -515,7 +484,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
                ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle,
                                           &kdev->kobj, "state%d", i);
                if (ret) {
-                       kfree(kobj);
+                       kobject_put(&kobj->kobj);
                        goto error_state;
                }
                cpuidle_add_s2idle_attr_group(kobj);
@@ -646,7 +615,7 @@ static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev)
        ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle,
                                   &kdev->kobj, "driver");
        if (ret) {
-               kfree(kdrv);
+               kobject_put(&kdrv->kobj);
                return ret;
        }
 
@@ -740,7 +709,7 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev)
        error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj,
                                   "cpuidle");
        if (error) {
-               kfree(kdev);
+               kobject_put(&kdev->kobj);
                return error;
        }
 
index a5fd8975f3d3c8bfed8f08175f3d06dc5b935713..a6abb701bfc6ec3c287fd5de20e4fad316159bd8 100644 (file)
@@ -8,7 +8,7 @@
  * This file add support for AES cipher with 128,192,256 bits keysize in
  * CBC and ECB mode.
  *
- * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ * You could find a link for the datasheet in Documentation/arm/sunxi.rst
  */
 
 #include <linux/crypto.h>
index 3e4e4bbda34c5091a5d6f702fc3b37cb159afe7b..b957061424a1f735f6640ed6919e766851c5298d 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Core file which registers crypto algorithms supported by the CryptoEngine.
  *
- * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ * You could find a link for the datasheet in Documentation/arm/sunxi.rst
  */
 #include <linux/clk.h>
 #include <linux/crypto.h>
index 84d52fc3a2da6e9175908c17e9b0431938a271d5..c89cb2ee24962f5473e20e10c7fc8e9d178095e6 100644 (file)
@@ -8,7 +8,7 @@
  * This file add support for AES cipher with 128,192,256 bits keysize in
  * CBC and ECB mode.
  *
- * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ * You could find a link for the datasheet in Documentation/arm/sunxi.rst
  */
 
 #include <linux/crypto.h>
index 6b301afffd1184925a5e771a7bfe54c311b1f2e2..5d9d0fedcb0689cfad429b909b01328c4dc0a889 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Core file which registers crypto algorithms supported by the SecuritySystem
  *
- * You could find a link for the datasheet in Documentation/arm/sunxi/README
+ * You could find a link for the datasheet in Documentation/arm/sunxi.rst
  */
 #include <linux/clk.h>
 #include <linux/crypto.h>
@@ -537,10 +537,8 @@ static int sun8i_ss_probe(struct platform_device *pdev)
                return err;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_err(ss->dev, "Cannot get SecuritySystem IRQ\n");
+       if (irq < 0)
                return irq;
-       }
 
        ss->reset = devm_reset_control_get(&pdev->dev, NULL);
        if (IS_ERR(ss->reset)) {
index 9d4ead2f7ebb3566ba5792a5b126e0d4e3ad1335..411857fad8ba60ca9f6aba973d05e426a51b2197 100644 (file)
@@ -253,10 +253,8 @@ static int meson_crypto_probe(struct platform_device *pdev)
        mc->irqs = devm_kcalloc(mc->dev, MAXFLOW, sizeof(int), GFP_KERNEL);
        for (i = 0; i < MAXFLOW; i++) {
                mc->irqs[i] = platform_get_irq(pdev, i);
-               if (mc->irqs[i] < 0) {
-                       dev_err(mc->dev, "Cannot get IRQ for flow %d\n", i);
+               if (mc->irqs[i] < 0)
                        return mc->irqs[i];
-               }
 
                err = devm_request_irq(&pdev->dev, mc->irqs[i], meson_irq_handler, 0,
                                       "gxl-crypto", mc);
index e536e2a6bbd8536395a9dc1e1a4bab52232b9e8d..75ccf41a7cb97a2584b525dffbd61ec7ae1383cb 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/of_device.h>
 #include <linux/delay.h>
 #include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/algapi.h>
 #include <crypto/sha.h>
index fcf1effc7661ec1437555bf2603fc828867b494a..62ba0325a61871f7827540ac0cef61b07f3974b0 100644 (file)
@@ -2239,16 +2239,12 @@ artpec6_crypto_hash_set_key(struct crypto_ahash *tfm,
        blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
 
        if (keylen > blocksize) {
-               SHASH_DESC_ON_STACK(hdesc, tfm_ctx->child_hash);
-
-               hdesc->tfm = tfm_ctx->child_hash;
-
                tfm_ctx->hmac_key_length = blocksize;
-               ret = crypto_shash_digest(hdesc, key, keylen,
-                                         tfm_ctx->hmac_key);
+
+               ret = crypto_shash_tfm_digest(tfm_ctx->child_hash, key, keylen,
+                                             tfm_ctx->hmac_key);
                if (ret)
                        return ret;
-
        } else {
                memcpy(tfm_ctx->hmac_key, key, keylen);
                tfm_ctx->hmac_key_length = keylen;
index c8b9408541a95ac1fa937ca80ee03bd946d0df97..a353217a0d33e263ce34285cee88491f629ff325 100644 (file)
@@ -308,9 +308,9 @@ static int handle_skcipher_req(struct iproc_reqctx_s *rctx)
            container_of(areq, struct skcipher_request, base);
        struct iproc_ctx_s *ctx = rctx->ctx;
        struct spu_cipher_parms cipher_parms;
-       int err = 0;
-       unsigned int chunksize = 0;     /* Num bytes of request to submit */
-       int remaining = 0;      /* Bytes of request still to process */
+       int err;
+       unsigned int chunksize; /* Num bytes of request to submit */
+       int remaining;  /* Bytes of request still to process */
        int chunk_start;        /* Beginning of data for current SPU msg */
 
        /* IV or ctr value to use in this SPU msg */
@@ -698,7 +698,7 @@ static int handle_ahash_req(struct iproc_reqctx_s *rctx)
 
        /* number of bytes still to be hashed in this req */
        unsigned int nbytes_to_hash = 0;
-       int err = 0;
+       int err;
        unsigned int chunksize = 0;     /* length of hash carry + new data */
        /*
         * length of new data, not from hash carry, to be submitted in
@@ -1664,7 +1664,7 @@ static void spu_rx_callback(struct mbox_client *cl, void *msg)
        struct spu_hw *spu = &iproc_priv.spu;
        struct brcm_message *mssg = msg;
        struct iproc_reqctx_s *rctx;
-       int err = 0;
+       int err;
 
        rctx = mssg->ctx;
        if (unlikely(!rctx)) {
@@ -1967,7 +1967,7 @@ static int ahash_enqueue(struct ahash_request *req)
        struct iproc_reqctx_s *rctx = ahash_request_ctx(req);
        struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
        struct iproc_ctx_s *ctx = crypto_ahash_ctx(tfm);
-       int err = 0;
+       int err;
        const char *alg_name;
 
        flow_log("ahash_enqueue() nbytes:%u\n", req->nbytes);
@@ -2299,7 +2299,7 @@ ahash_finup_exit:
 
 static int ahash_digest(struct ahash_request *req)
 {
-       int err = 0;
+       int err;
 
        flow_log("ahash_digest() nbytes:%u\n", req->nbytes);
 
@@ -4436,7 +4436,7 @@ static int spu_mb_init(struct device *dev)
        for (i = 0; i < iproc_priv.spu.num_chan; i++) {
                iproc_priv.mbox[i] = mbox_request_channel(mcl, i);
                if (IS_ERR(iproc_priv.mbox[i])) {
-                       err = (int)PTR_ERR(iproc_priv.mbox[i]);
+                       err = PTR_ERR(iproc_priv.mbox[i]);
                        dev_err(dev,
                                "Mbox channel %d request failed with err %d",
                                i, err);
@@ -4717,21 +4717,20 @@ static int spu_dt_read(struct platform_device *pdev)
 
        matched_spu_type = of_device_get_match_data(dev);
        if (!matched_spu_type) {
-               dev_err(&pdev->dev, "Failed to match device\n");
+               dev_err(dev, "Failed to match device\n");
                return -ENODEV;
        }
 
        spu->spu_type = matched_spu_type->type;
        spu->spu_subtype = matched_spu_type->subtype;
 
-       i = 0;
        for (i = 0; (i < MAX_SPUS) && ((spu_ctrl_regs =
                platform_get_resource(pdev, IORESOURCE_MEM, i)) != NULL); i++) {
 
                spu->reg_vbase[i] = devm_ioremap_resource(dev, spu_ctrl_regs);
                if (IS_ERR(spu->reg_vbase[i])) {
                        err = PTR_ERR(spu->reg_vbase[i]);
-                       dev_err(&pdev->dev, "Failed to map registers: %d\n",
+                       dev_err(dev, "Failed to map registers: %d\n",
                                err);
                        spu->reg_vbase[i] = NULL;
                        return err;
@@ -4747,7 +4746,7 @@ static int bcm_spu_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct spu_hw *spu = &iproc_priv.spu;
-       int err = 0;
+       int err;
 
        iproc_priv.pdev  = pdev;
        platform_set_drvdata(iproc_priv.pdev,
@@ -4757,7 +4756,7 @@ static int bcm_spu_probe(struct platform_device *pdev)
        if (err < 0)
                goto failure;
 
-       err = spu_mb_init(&pdev->dev);
+       err = spu_mb_init(dev);
        if (err < 0)
                goto failure;
 
@@ -4766,7 +4765,7 @@ static int bcm_spu_probe(struct platform_device *pdev)
        else if (spu->spu_type == SPU_TYPE_SPU2)
                iproc_priv.bcm_hdr_len = 0;
 
-       spu_functions_register(&pdev->dev, spu->spu_type, spu->spu_subtype);
+       spu_functions_register(dev, spu->spu_type, spu->spu_subtype);
 
        spu_counters_init();
 
index e91be9b8b083e534882339d07537f1cad57ce47d..788c6607078b1e990c563f2470ef482776aeee2f 100644 (file)
@@ -346,7 +346,7 @@ static void nitrox_pf_sw_cleanup(struct nitrox_device *ndev)
 }
 
 /**
- * nitrox_bist_check - Check NITORX BIST registers status
+ * nitrox_bist_check - Check NITROX BIST registers status
  * @ndev: NITROX device
  */
 static int nitrox_bist_check(struct nitrox_device *ndev)
index e0a8bd15aa7473c242f741c8229d0700f18e09ab..32268e239bf15e49c9749e3fd5e9640ea09056a0 100644 (file)
@@ -10,10 +10,9 @@ config CRYPTO_DEV_CCP_DD
 config CRYPTO_DEV_SP_CCP
        bool "Cryptographic Coprocessor device"
        default y
-       depends on CRYPTO_DEV_CCP_DD
+       depends on CRYPTO_DEV_CCP_DD && DMADEVICES
        select HW_RANDOM
        select DMA_ENGINE
-       select DMADEVICES
        select CRYPTO_SHA1
        select CRYPTO_SHA256
        help
index 474e6f1a6a84ecf7998ba4b1b7945ff09e143c09..b0cc2bd73af80409253d4d601192576df0b3385d 100644 (file)
@@ -272,9 +272,6 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
 {
        struct ccp_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
        struct crypto_shash *shash = ctx->u.sha.hmac_tfm;
-
-       SHASH_DESC_ON_STACK(sdesc, shash);
-
        unsigned int block_size = crypto_shash_blocksize(shash);
        unsigned int digest_size = crypto_shash_digestsize(shash);
        int i, ret;
@@ -289,10 +286,8 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
 
        if (key_len > block_size) {
                /* Must hash the input key */
-               sdesc->tfm = shash;
-
-               ret = crypto_shash_digest(sdesc, key, key_len,
-                                         ctx->u.sha.key);
+               ret = crypto_shash_tfm_digest(shash, key, key_len,
+                                             ctx->u.sha.key);
                if (ret)
                        return -EINVAL;
 
index 896f190b9a502562557f9575c6d3f585eacdc794..a2426334be61205167f838f13527b130bbb64732 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/hw_random.h>
 #include <linux/ccp.h>
 #include <linux/firmware.h>
+#include <linux/gfp.h>
 
 #include <asm/smp.h>
 
@@ -44,6 +45,14 @@ MODULE_PARM_DESC(psp_probe_timeout, " default timeout value, in seconds, during
 static bool psp_dead;
 static int psp_timeout;
 
+/* Trusted Memory Region (TMR):
+ *   The TMR is a 1MB area that must be 1MB aligned.  Use the page allocator
+ *   to allocate the memory, which will return aligned memory for the specified
+ *   allocation order.
+ */
+#define SEV_ES_TMR_SIZE                (1024 * 1024)
+static void *sev_es_tmr;
+
 static inline bool sev_version_greater_or_equal(u8 maj, u8 min)
 {
        struct sev_device *sev = psp_master->sev_data;
@@ -214,6 +223,20 @@ static int __sev_platform_init_locked(int *error)
        if (sev->state == SEV_STATE_INIT)
                return 0;
 
+       if (sev_es_tmr) {
+               u64 tmr_pa;
+
+               /*
+                * Do not include the encryption mask on the physical
+                * address of the TMR (firmware should clear it anyway).
+                */
+               tmr_pa = __pa(sev_es_tmr);
+
+               sev->init_cmd_buf.flags |= SEV_INIT_FLAGS_SEV_ES;
+               sev->init_cmd_buf.tmr_address = tmr_pa;
+               sev->init_cmd_buf.tmr_len = SEV_ES_TMR_SIZE;
+       }
+
        rc = __sev_do_cmd_locked(SEV_CMD_INIT, &sev->init_cmd_buf, error);
        if (rc)
                return rc;
@@ -371,8 +394,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable)
                goto cmd;
 
        /* allocate a physically contiguous buffer to store the CSR blob */
-       if (!access_ok(input.address, input.length) ||
-           input.length > SEV_FW_BLOB_MAX_SIZE) {
+       if (input.length > SEV_FW_BLOB_MAX_SIZE) {
                ret = -EFAULT;
                goto e_free;
        }
@@ -609,12 +631,6 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp)
        if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))
                return -EFAULT;
 
-       /* Check if we have write access to the userspace buffer */
-       if (input.address &&
-           input.length &&
-           !access_ok(input.address, input.length))
-               return -EFAULT;
-
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
@@ -730,15 +746,13 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable)
                goto cmd;
 
        /* Allocate a physically contiguous buffer to store the PDH blob. */
-       if ((input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) ||
-           !access_ok(input.pdh_cert_address, input.pdh_cert_len)) {
+       if (input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) {
                ret = -EFAULT;
                goto e_free;
        }
 
        /* Allocate a physically contiguous buffer to store the cert chain blob. */
-       if ((input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) ||
-           !access_ok(input.cert_chain_address, input.cert_chain_len)) {
+       if (input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) {
                ret = -EFAULT;
                goto e_free;
        }
@@ -1012,6 +1026,7 @@ EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
 void sev_pci_init(void)
 {
        struct sev_device *sev = psp_master->sev_data;
+       struct page *tmr_page;
        int error, rc;
 
        if (!sev)
@@ -1041,6 +1056,16 @@ void sev_pci_init(void)
            sev_update_firmware(sev->dev) == 0)
                sev_get_api_version();
 
+       /* Obtain the TMR memory area for SEV-ES use */
+       tmr_page = alloc_pages(GFP_KERNEL, get_order(SEV_ES_TMR_SIZE));
+       if (tmr_page) {
+               sev_es_tmr = page_address(tmr_page);
+       } else {
+               sev_es_tmr = NULL;
+               dev_warn(sev->dev,
+                        "SEV: TMR allocation failed, SEV-ES support unavailable\n");
+       }
+
        /* Initialize the platform */
        rc = sev_platform_init(&error);
        if (rc && (error == SEV_RET_SECURE_DATA_INVALID)) {
@@ -1075,4 +1100,13 @@ void sev_pci_exit(void)
                return;
 
        sev_platform_shutdown(NULL);
+
+       if (sev_es_tmr) {
+               /* The TMR area was encrypted, flush it from the cache */
+               wbinvd_on_all_cpus();
+
+               free_pages((unsigned long)sev_es_tmr,
+                          get_order(SEV_ES_TMR_SIZE));
+               sev_es_tmr = NULL;
+       }
 }
index a84335328f371c28684cb0d7b2de1da9da6c095c..872ea3ff1c6ba73f660479efb254a36ac6d405f6 100644 (file)
@@ -427,12 +427,9 @@ static int cc_cipher_setkey(struct crypto_skcipher *sktfm, const u8 *key,
                int key_len = keylen >> 1;
                int err;
 
-               SHASH_DESC_ON_STACK(desc, ctx_p->shash_tfm);
-
-               desc->tfm = ctx_p->shash_tfm;
-
-               err = crypto_shash_digest(desc, ctx_p->user.key, key_len,
-                                         ctx_p->user.key + key_len);
+               err = crypto_shash_tfm_digest(ctx_p->shash_tfm,
+                                             ctx_p->user.key, key_len,
+                                             ctx_p->user.key + key_len);
                if (err) {
                        dev_err(dev, "Failed to hash ESSIV key.\n");
                        return err;
index c454afce7781017be7087a98f6b8a273ec226f4a..7083767602fcf98ad36051aa4279792aa9a0426c 100644 (file)
@@ -26,7 +26,7 @@ static struct debugfs_reg32 ver_sig_regs[] = {
        { .name = "VERSION" }, /* Must be 1st */
 };
 
-static struct debugfs_reg32 pid_cid_regs[] = {
+static const struct debugfs_reg32 pid_cid_regs[] = {
        CC_DEBUG_REG(PERIPHERAL_ID_0),
        CC_DEBUG_REG(PERIPHERAL_ID_1),
        CC_DEBUG_REG(PERIPHERAL_ID_2),
@@ -38,7 +38,7 @@ static struct debugfs_reg32 pid_cid_regs[] = {
        CC_DEBUG_REG(COMPONENT_ID_3),
 };
 
-static struct debugfs_reg32 debug_regs[] = {
+static const struct debugfs_reg32 debug_regs[] = {
        CC_DEBUG_REG(HOST_IRR),
        CC_DEBUG_REG(HOST_POWER_DOWN_EN),
        CC_DEBUG_REG(AXIM_MON_ERR),
index c29b80dd30d86c5a332654fdb6f5748883d497a8..caf1136e7ef98b092068dd5425c7dce8a4adb92b 100644 (file)
@@ -44,7 +44,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 #include <linux/highmem.h>
@@ -1757,7 +1756,7 @@ static int chcr_ahash_final(struct ahash_request *req)
        struct uld_ctx *u_ctx = ULD_CTX(h_ctx(rtfm));
        struct chcr_context *ctx = h_ctx(rtfm);
        u8 bs = crypto_tfm_alg_blocksize(crypto_ahash_tfm(rtfm));
-       int error = -EINVAL;
+       int error;
        unsigned int cpu;
 
        cpu = get_cpu();
index 9fd3b9d1ec2f5d54379542f56927c6152e15d644..25bf6d963066d17d3cb9e7ec6b9bdeeb7120c389 100644 (file)
@@ -40,7 +40,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <linux/skbuff.h>
 #include <linux/rtnetlink.h>
 #include <linux/highmem.h>
index dccef3a2908b391e772944d504d953a062001d9f..e1401d9cc756cea07f7ad310c17fed29e0f3e9db 100644 (file)
@@ -682,7 +682,7 @@ int chtls_push_frames(struct chtls_sock *csk, int comp)
                                make_tx_data_wr(sk, skb, immdlen, len,
                                                credits_needed, completion);
                        tp->snd_nxt += len;
-                       tp->lsndtime = tcp_time_stamp(tp);
+                       tp->lsndtime = tcp_jiffies32;
                        if (completion)
                                ULP_SKB_CB(skb)->flags &= ~ULPCB_FLAG_NEED_HDR;
                } else {
index f09c6cf7823e93934a14db1b1b0f3fa3753b4ab0..9c3b3ca815e66b512c35583efa877199d8721272 100644 (file)
@@ -29,6 +29,7 @@ config CRYPTO_DEV_HISI_SEC2
        depends on PCI && PCI_MSI
        depends on UACCE || UACCE=n
        depends on ARM64 || (COMPILE_TEST && 64BIT)
+       depends on ACPI
        help
          Support for HiSilicon SEC Engine of version 2 in crypto subsystem.
          It provides AES, SM4, and 3DES algorithms with ECB
@@ -42,6 +43,7 @@ config CRYPTO_DEV_HISI_QM
        depends on ARM64 || COMPILE_TEST
        depends on PCI && PCI_MSI
        depends on UACCE || UACCE=n
+       depends on ACPI
        help
          HiSilicon accelerator engines use a common queue management
          interface. Specific engine driver may use this module.
@@ -52,6 +54,7 @@ config CRYPTO_DEV_HISI_ZIP
        depends on ARM64 || (COMPILE_TEST && 64BIT)
        depends on !CPU_BIG_ENDIAN || COMPILE_TEST
        depends on UACCE || UACCE=n
+       depends on ACPI
        select CRYPTO_DEV_HISI_QM
        help
          Support for HiSilicon ZIP Driver
@@ -61,6 +64,7 @@ config CRYPTO_DEV_HISI_HPRE
        depends on PCI && PCI_MSI
        depends on UACCE || UACCE=n
        depends on ARM64 || (COMPILE_TEST && 64BIT)
+       depends on ACPI
        select CRYPTO_DEV_HISI_QM
        select CRYPTO_DH
        select CRYPTO_RSA
index 03d512ec633609f79b90c9e762764d3f72a2aa56..ed730d173e95a5b41c55d0b1d3eb5eb8c96b3c9c 100644 (file)
@@ -25,6 +25,17 @@ enum hpre_ctrl_dbgfs_file {
        HPRE_DEBUG_FILE_NUM,
 };
 
+enum hpre_dfx_dbgfs_file {
+       HPRE_SEND_CNT,
+       HPRE_RECV_CNT,
+       HPRE_SEND_FAIL_CNT,
+       HPRE_SEND_BUSY_CNT,
+       HPRE_OVER_THRHLD_CNT,
+       HPRE_OVERTIME_THRHLD,
+       HPRE_INVALID_REQ_CNT,
+       HPRE_DFX_FILE_NUM
+};
+
 #define HPRE_DEBUGFS_FILE_NUM    (HPRE_DEBUG_FILE_NUM + HPRE_CLUSTERS_NUM - 1)
 
 struct hpre_debugfs_file {
@@ -34,6 +45,11 @@ struct hpre_debugfs_file {
        struct hpre_debug *debug;
 };
 
+struct hpre_dfx {
+       atomic64_t value;
+       enum hpre_dfx_dbgfs_file type;
+};
+
 /*
  * One HPRE controller has one PF and multiple VFs, some global configurations
  * which PF has need this structure.
@@ -41,13 +57,13 @@ struct hpre_debugfs_file {
  */
 struct hpre_debug {
        struct dentry *debug_root;
+       struct hpre_dfx dfx[HPRE_DFX_FILE_NUM];
        struct hpre_debugfs_file files[HPRE_DEBUGFS_FILE_NUM];
 };
 
 struct hpre {
        struct hisi_qm qm;
        struct hpre_debug debug;
-       u32 num_vfs;
        unsigned long status;
 };
 
index 65425250b2e99352038d9a28aa30e54c3190988f..7b5cb27d473d2990db42bed317c59bc20e920101 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/fips.h>
 #include <linux/module.h>
+#include <linux/time.h>
 #include "hpre.h"
 
 struct hpre_ctx;
@@ -32,6 +33,9 @@ struct hpre_ctx;
 #define HPRE_SQE_DONE_SHIFT    30
 #define HPRE_DH_MAX_P_SZ       512
 
+#define HPRE_DFX_SEC_TO_US     1000000
+#define HPRE_DFX_US_TO_NS      1000
+
 typedef void (*hpre_cb)(struct hpre_ctx *ctx, void *sqe);
 
 struct hpre_rsa_ctx {
@@ -68,6 +72,7 @@ struct hpre_dh_ctx {
 struct hpre_ctx {
        struct hisi_qp *qp;
        struct hpre_asym_request **req_list;
+       struct hpre *hpre;
        spinlock_t req_lock;
        unsigned int key_sz;
        bool crt_g2_mode;
@@ -90,6 +95,7 @@ struct hpre_asym_request {
        int err;
        int req_id;
        hpre_cb cb;
+       struct timespec64 req_time;
 };
 
 static DEFINE_MUTEX(hpre_alg_lock);
@@ -119,6 +125,7 @@ static void hpre_free_req_id(struct hpre_ctx *ctx, int req_id)
 static int hpre_add_req_to_ctx(struct hpre_asym_request *hpre_req)
 {
        struct hpre_ctx *ctx;
+       struct hpre_dfx *dfx;
        int id;
 
        ctx = hpre_req->ctx;
@@ -129,6 +136,10 @@ static int hpre_add_req_to_ctx(struct hpre_asym_request *hpre_req)
        ctx->req_list[id] = hpre_req;
        hpre_req->req_id = id;
 
+       dfx = ctx->hpre->debug.dfx;
+       if (atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value))
+               ktime_get_ts64(&hpre_req->req_time);
+
        return id;
 }
 
@@ -309,12 +320,16 @@ static int hpre_alg_res_post_hf(struct hpre_ctx *ctx, struct hpre_sqe *sqe,
 
 static int hpre_ctx_set(struct hpre_ctx *ctx, struct hisi_qp *qp, int qlen)
 {
+       struct hpre *hpre;
+
        if (!ctx || !qp || qlen < 0)
                return -EINVAL;
 
        spin_lock_init(&ctx->req_lock);
        ctx->qp = qp;
 
+       hpre = container_of(ctx->qp->qm, struct hpre, qm);
+       ctx->hpre = hpre;
        ctx->req_list = kcalloc(qlen, sizeof(void *), GFP_KERNEL);
        if (!ctx->req_list)
                return -ENOMEM;
@@ -337,38 +352,80 @@ static void hpre_ctx_clear(struct hpre_ctx *ctx, bool is_clear_all)
        ctx->key_sz = 0;
 }
 
+static bool hpre_is_bd_timeout(struct hpre_asym_request *req,
+                              u64 overtime_thrhld)
+{
+       struct timespec64 reply_time;
+       u64 time_use_us;
+
+       ktime_get_ts64(&reply_time);
+       time_use_us = (reply_time.tv_sec - req->req_time.tv_sec) *
+               HPRE_DFX_SEC_TO_US +
+               (reply_time.tv_nsec - req->req_time.tv_nsec) /
+               HPRE_DFX_US_TO_NS;
+
+       if (time_use_us <= overtime_thrhld)
+               return false;
+
+       return true;
+}
+
 static void hpre_dh_cb(struct hpre_ctx *ctx, void *resp)
 {
+       struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
        struct hpre_asym_request *req;
        struct kpp_request *areq;
+       u64 overtime_thrhld;
        int ret;
 
        ret = hpre_alg_res_post_hf(ctx, resp, (void **)&req);
        areq = req->areq.dh;
        areq->dst_len = ctx->key_sz;
+
+       overtime_thrhld = atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value);
+       if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld))
+               atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value);
+
        hpre_hw_data_clr_all(ctx, req, areq->dst, areq->src);
        kpp_request_complete(areq, ret);
+       atomic64_inc(&dfx[HPRE_RECV_CNT].value);
 }
 
 static void hpre_rsa_cb(struct hpre_ctx *ctx, void *resp)
 {
+       struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
        struct hpre_asym_request *req;
        struct akcipher_request *areq;
+       u64 overtime_thrhld;
        int ret;
 
        ret = hpre_alg_res_post_hf(ctx, resp, (void **)&req);
+
+       overtime_thrhld = atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value);
+       if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld))
+               atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value);
+
        areq = req->areq.rsa;
        areq->dst_len = ctx->key_sz;
        hpre_hw_data_clr_all(ctx, req, areq->dst, areq->src);
        akcipher_request_complete(areq, ret);
+       atomic64_inc(&dfx[HPRE_RECV_CNT].value);
 }
 
 static void hpre_alg_cb(struct hisi_qp *qp, void *resp)
 {
        struct hpre_ctx *ctx = qp->qp_ctx;
+       struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
        struct hpre_sqe *sqe = resp;
+       struct hpre_asym_request *req = ctx->req_list[le16_to_cpu(sqe->tag)];
 
-       ctx->req_list[le16_to_cpu(sqe->tag)]->cb(ctx, resp);
+
+       if (unlikely(!req)) {
+               atomic64_inc(&dfx[HPRE_INVALID_REQ_CNT].value);
+               return;
+       }
+
+       req->cb(ctx, resp);
 }
 
 static int hpre_ctx_init(struct hpre_ctx *ctx)
@@ -436,6 +493,29 @@ static int hpre_msg_request_set(struct hpre_ctx *ctx, void *req, bool is_rsa)
        return 0;
 }
 
+static int hpre_send(struct hpre_ctx *ctx, struct hpre_sqe *msg)
+{
+       struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
+       int ctr = 0;
+       int ret;
+
+       do {
+               atomic64_inc(&dfx[HPRE_SEND_CNT].value);
+               ret = hisi_qp_send(ctx->qp, msg);
+               if (ret != -EBUSY)
+                       break;
+               atomic64_inc(&dfx[HPRE_SEND_BUSY_CNT].value);
+       } while (ctr++ < HPRE_TRY_SEND_TIMES);
+
+       if (likely(!ret))
+               return ret;
+
+       if (ret != -EBUSY)
+               atomic64_inc(&dfx[HPRE_SEND_FAIL_CNT].value);
+
+       return ret;
+}
+
 #ifdef CONFIG_CRYPTO_DH
 static int hpre_dh_compute_value(struct kpp_request *req)
 {
@@ -444,7 +524,6 @@ static int hpre_dh_compute_value(struct kpp_request *req)
        void *tmp = kpp_request_ctx(req);
        struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, HPRE_ALIGN_SZ);
        struct hpre_sqe *msg = &hpre_req->req;
-       int ctr = 0;
        int ret;
 
        ret = hpre_msg_request_set(ctx, req, false);
@@ -465,11 +544,9 @@ static int hpre_dh_compute_value(struct kpp_request *req)
                msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) | HPRE_ALG_DH_G2);
        else
                msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) | HPRE_ALG_DH);
-       do {
-               ret = hisi_qp_send(ctx->qp, msg);
-       } while (ret == -EBUSY && ctr++ < HPRE_TRY_SEND_TIMES);
 
        /* success */
+       ret = hpre_send(ctx, msg);
        if (likely(!ret))
                return -EINPROGRESS;
 
@@ -647,7 +724,6 @@ static int hpre_rsa_enc(struct akcipher_request *req)
        void *tmp = akcipher_request_ctx(req);
        struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, HPRE_ALIGN_SZ);
        struct hpre_sqe *msg = &hpre_req->req;
-       int ctr = 0;
        int ret;
 
        /* For 512 and 1536 bits key size, use soft tfm instead */
@@ -677,11 +753,8 @@ static int hpre_rsa_enc(struct akcipher_request *req)
        if (unlikely(ret))
                goto clear_all;
 
-       do {
-               ret = hisi_qp_send(ctx->qp, msg);
-       } while (ret == -EBUSY && ctr++ < HPRE_TRY_SEND_TIMES);
-
        /* success */
+       ret = hpre_send(ctx, msg);
        if (likely(!ret))
                return -EINPROGRESS;
 
@@ -699,7 +772,6 @@ static int hpre_rsa_dec(struct akcipher_request *req)
        void *tmp = akcipher_request_ctx(req);
        struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, HPRE_ALIGN_SZ);
        struct hpre_sqe *msg = &hpre_req->req;
-       int ctr = 0;
        int ret;
 
        /* For 512 and 1536 bits key size, use soft tfm instead */
@@ -736,11 +808,8 @@ static int hpre_rsa_dec(struct akcipher_request *req)
        if (unlikely(ret))
                goto clear_all;
 
-       do {
-               ret = hisi_qp_send(ctx->qp, msg);
-       } while (ret == -EBUSY && ctr++ < HPRE_TRY_SEND_TIMES);
-
        /* success */
+       ret = hpre_send(ctx, msg);
        if (likely(!ret))
                return -EINPROGRESS;
 
index 88be53bf4a38a944cad5b34fb8e06b52fd4ffdad..a3ee127a70e391fe2e5b92fce598378241634b04 100644 (file)
 #define HPRE_HAC_ECC2_CNT              0x301a08
 #define HPRE_HAC_INT_STATUS            0x301800
 #define HPRE_HAC_SOURCE_INT            0x301600
-#define MASTER_GLOBAL_CTRL_SHUTDOWN    1
-#define MASTER_TRANS_RETURN_RW         3
-#define HPRE_MASTER_TRANS_RETURN       0x300150
-#define HPRE_MASTER_GLOBAL_CTRL                0x300000
 #define HPRE_CLSTR_ADDR_INTRVL         0x1000
 #define HPRE_CLUSTER_INQURY            0x100
 #define HPRE_CLSTR_ADDR_INQRY_RSLT     0x104
 #define HPRE_BD_USR_MASK               0x3
 #define HPRE_CLUSTER_CORE_MASK         0xf
 
+#define HPRE_AM_OOO_SHUTDOWN_ENB       0x301044
+#define HPRE_AM_OOO_SHUTDOWN_ENABLE    BIT(0)
+#define HPRE_WR_MSI_PORT               BIT(2)
+
+#define HPRE_CORE_ECC_2BIT_ERR         BIT(1)
+#define HPRE_OOO_ECC_2BIT_ERR          BIT(5)
+
 #define HPRE_VIA_MSI_DSM               1
+#define HPRE_SQE_MASK_OFFSET           8
+#define HPRE_SQE_MASK_LEN              24
 
 static struct hisi_qm_list hpre_devices;
 static const char hpre_name[] = "hisi_hpre";
@@ -131,7 +136,7 @@ static const u64 hpre_cluster_offsets[] = {
                HPRE_CLSTR_BASE + HPRE_CLUSTER3 * HPRE_CLSTR_ADDR_INTRVL,
 };
 
-static struct debugfs_reg32 hpre_cluster_dfx_regs[] = {
+static const struct debugfs_reg32 hpre_cluster_dfx_regs[] = {
        {"CORES_EN_STATUS          ",  HPRE_CORE_EN_OFFSET},
        {"CORES_INI_CFG              ",  HPRE_CORE_INI_CFG_OFFSET},
        {"CORES_INI_STATUS         ",  HPRE_CORE_INI_STATUS_OFFSET},
@@ -139,7 +144,7 @@ static struct debugfs_reg32 hpre_cluster_dfx_regs[] = {
        {"CORES_IS_SCHD               ",  HPRE_CORE_IS_SCHD_OFFSET},
 };
 
-static struct debugfs_reg32 hpre_com_dfx_regs[] = {
+static const struct debugfs_reg32 hpre_com_dfx_regs[] = {
        {"READ_CLR_EN          ",  HPRE_CTRL_CNT_CLR_CE},
        {"AXQOS                   ",  HPRE_VFG_AXQOS},
        {"AWUSR_CFG              ",  HPRE_AWUSR_FP_CFG},
@@ -156,44 +161,38 @@ static struct debugfs_reg32 hpre_com_dfx_regs[] = {
        {"INT_STATUS               ",  HPRE_INT_STATUS},
 };
 
-static int hpre_pf_q_num_set(const char *val, const struct kernel_param *kp)
-{
-       struct pci_dev *pdev;
-       u32 n, q_num;
-       u8 rev_id;
-       int ret;
-
-       if (!val)
-               return -EINVAL;
-
-       pdev = pci_get_device(PCI_VENDOR_ID_HUAWEI, HPRE_PCI_DEVICE_ID, NULL);
-       if (!pdev) {
-               q_num = HPRE_QUEUE_NUM_V2;
-               pr_info("No device found currently, suppose queue number is %d\n",
-                       q_num);
-       } else {
-               rev_id = pdev->revision;
-               if (rev_id != QM_HW_V2)
-                       return -EINVAL;
-
-               q_num = HPRE_QUEUE_NUM_V2;
-       }
-
-       ret = kstrtou32(val, 10, &n);
-       if (ret != 0 || n == 0 || n > q_num)
-               return -EINVAL;
+static const char *hpre_dfx_files[HPRE_DFX_FILE_NUM] = {
+       "send_cnt",
+       "recv_cnt",
+       "send_fail_cnt",
+       "send_busy_cnt",
+       "over_thrhld_cnt",
+       "overtime_thrhld",
+       "invalid_req_cnt"
+};
 
-       return param_set_int(val, kp);
+static int pf_q_num_set(const char *val, const struct kernel_param *kp)
+{
+       return q_num_set(val, kp, HPRE_PCI_DEVICE_ID);
 }
 
 static const struct kernel_param_ops hpre_pf_q_num_ops = {
-       .set = hpre_pf_q_num_set,
+       .set = pf_q_num_set,
        .get = param_get_int,
 };
 
-static u32 hpre_pf_q_num = HPRE_PF_DEF_Q_NUM;
-module_param_cb(hpre_pf_q_num, &hpre_pf_q_num_ops, &hpre_pf_q_num, 0444);
-MODULE_PARM_DESC(hpre_pf_q_num, "Number of queues in PF of CS(1-1024)");
+static u32 pf_q_num = HPRE_PF_DEF_Q_NUM;
+module_param_cb(pf_q_num, &hpre_pf_q_num_ops, &pf_q_num, 0444);
+MODULE_PARM_DESC(pf_q_num, "Number of queues in PF of CS(1-1024)");
+
+static const struct kernel_param_ops vfs_num_ops = {
+       .set = vfs_num_set,
+       .get = param_get_int,
+};
+
+static u32 vfs_num;
+module_param_cb(vfs_num, &vfs_num_ops, &vfs_num, 0444);
+MODULE_PARM_DESC(vfs_num, "Number of VFs to enable(1-63), 0(default)");
 
 struct hisi_qp *hpre_create_qp(void)
 {
@@ -232,9 +231,8 @@ static int hpre_cfg_by_dsm(struct hisi_qm *qm)
        return 0;
 }
 
-static int hpre_set_user_domain_and_cache(struct hpre *hpre)
+static int hpre_set_user_domain_and_cache(struct hisi_qm *qm)
 {
-       struct hisi_qm *qm = &hpre->qm;
        struct device *dev = &qm->pdev->dev;
        unsigned long offset;
        int ret, i;
@@ -324,17 +322,34 @@ static void hpre_cnt_regs_clear(struct hisi_qm *qm)
 
 static void hpre_hw_error_disable(struct hisi_qm *qm)
 {
+       u32 val;
+
        /* disable hpre hw error interrupts */
        writel(HPRE_CORE_INT_DISABLE, qm->io_base + HPRE_INT_MASK);
+
+       /* disable HPRE block master OOO when m-bit error occur */
+       val = readl(qm->io_base + HPRE_AM_OOO_SHUTDOWN_ENB);
+       val &= ~HPRE_AM_OOO_SHUTDOWN_ENABLE;
+       writel(val, qm->io_base + HPRE_AM_OOO_SHUTDOWN_ENB);
 }
 
 static void hpre_hw_error_enable(struct hisi_qm *qm)
 {
+       u32 val;
+
+       /* clear HPRE hw error source if having */
+       writel(HPRE_CORE_INT_DISABLE, qm->io_base + HPRE_HAC_SOURCE_INT);
+
        /* enable hpre hw error interrupts */
        writel(HPRE_CORE_INT_ENABLE, qm->io_base + HPRE_INT_MASK);
        writel(HPRE_HAC_RAS_CE_ENABLE, qm->io_base + HPRE_RAS_CE_ENB);
        writel(HPRE_HAC_RAS_NFE_ENABLE, qm->io_base + HPRE_RAS_NFE_ENB);
        writel(HPRE_HAC_RAS_FE_ENABLE, qm->io_base + HPRE_RAS_FE_ENB);
+
+       /* enable HPRE block master OOO when m-bit error occur */
+       val = readl(qm->io_base + HPRE_AM_OOO_SHUTDOWN_ENB);
+       val |= HPRE_AM_OOO_SHUTDOWN_ENABLE;
+       writel(val, qm->io_base + HPRE_AM_OOO_SHUTDOWN_ENB);
 }
 
 static inline struct hisi_qm *hpre_file_to_qm(struct hpre_debugfs_file *file)
@@ -354,9 +369,7 @@ static u32 hpre_current_qm_read(struct hpre_debugfs_file *file)
 static int hpre_current_qm_write(struct hpre_debugfs_file *file, u32 val)
 {
        struct hisi_qm *qm = hpre_file_to_qm(file);
-       struct hpre_debug *debug = file->debug;
-       struct hpre *hpre = container_of(debug, struct hpre, debug);
-       u32 num_vfs = hpre->num_vfs;
+       u32 num_vfs = qm->vfs_num;
        u32 vfq_num, tmp;
 
 
@@ -523,6 +536,33 @@ static const struct file_operations hpre_ctrl_debug_fops = {
        .write = hpre_ctrl_debug_write,
 };
 
+static int hpre_debugfs_atomic64_get(void *data, u64 *val)
+{
+       struct hpre_dfx *dfx_item = data;
+
+       *val = atomic64_read(&dfx_item->value);
+
+       return 0;
+}
+
+static int hpre_debugfs_atomic64_set(void *data, u64 val)
+{
+       struct hpre_dfx *dfx_item = data;
+       struct hpre_dfx *hpre_dfx = dfx_item - HPRE_OVERTIME_THRHLD;
+
+       if (val)
+               return -EINVAL;
+
+       if (dfx_item->type == HPRE_OVERTIME_THRHLD)
+               atomic64_set(&hpre_dfx[HPRE_OVER_THRHLD_CNT].value, 0);
+       atomic64_set(&dfx_item->value, val);
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(hpre_atomic64_ops, hpre_debugfs_atomic64_get,
+                        hpre_debugfs_atomic64_set, "%llu\n");
+
 static int hpre_create_debugfs_file(struct hpre_debug *dbg, struct dentry *dir,
                                    enum hpre_ctrl_dbgfs_file type, int indx)
 {
@@ -620,6 +660,22 @@ static int hpre_ctrl_debug_init(struct hpre_debug *debug)
        return hpre_cluster_debugfs_init(debug);
 }
 
+static void hpre_dfx_debug_init(struct hpre_debug *debug)
+{
+       struct hpre *hpre = container_of(debug, struct hpre, debug);
+       struct hpre_dfx *dfx = hpre->debug.dfx;
+       struct hisi_qm *qm = &hpre->qm;
+       struct dentry *parent;
+       int i;
+
+       parent = debugfs_create_dir("hpre_dfx", qm->debug.debug_root);
+       for (i = 0; i < HPRE_DFX_FILE_NUM; i++) {
+               dfx[i].type = i;
+               debugfs_create_file(hpre_dfx_files[i], 0644, parent, &dfx[i],
+                                   &hpre_atomic64_ops);
+       }
+}
+
 static int hpre_debugfs_init(struct hpre *hpre)
 {
        struct hisi_qm *qm = &hpre->qm;
@@ -629,6 +685,8 @@ static int hpre_debugfs_init(struct hpre *hpre)
 
        dir = debugfs_create_dir(dev_name(dev), hpre_debugfs_root);
        qm->debug.debug_root = dir;
+       qm->debug.sqe_mask_offset = HPRE_SQE_MASK_OFFSET;
+       qm->debug.sqe_mask_len = HPRE_SQE_MASK_LEN;
 
        ret = hisi_qm_debug_init(qm);
        if (ret)
@@ -640,6 +698,9 @@ static int hpre_debugfs_init(struct hpre *hpre)
                if (ret)
                        goto failed_to_create;
        }
+
+       hpre_dfx_debug_init(&hpre->debug);
+
        return 0;
 
 failed_to_create:
@@ -654,32 +715,27 @@ static void hpre_debugfs_exit(struct hpre *hpre)
        debugfs_remove_recursive(qm->debug.debug_root);
 }
 
-static int hpre_qm_pre_init(struct hisi_qm *qm, struct pci_dev *pdev)
+static int hpre_qm_init(struct hisi_qm *qm, struct pci_dev *pdev)
 {
-       enum qm_hw_ver rev_id;
-
-       rev_id = hisi_qm_get_hw_version(pdev);
-       if (rev_id < 0)
-               return -ENODEV;
-
-       if (rev_id == QM_HW_V1) {
+       if (pdev->revision == QM_HW_V1) {
                pci_warn(pdev, "HPRE version 1 is not supported!\n");
                return -EINVAL;
        }
 
        qm->pdev = pdev;
-       qm->ver = rev_id;
+       qm->ver = pdev->revision;
        qm->sqe_size = HPRE_SQE_SIZE;
        qm->dev_name = hpre_name;
+
        qm->fun_type = (pdev->device == HPRE_PCI_DEVICE_ID) ?
-                      QM_HW_PF : QM_HW_VF;
-       if (pdev->is_physfn) {
+                       QM_HW_PF : QM_HW_VF;
+       if (qm->fun_type == QM_HW_PF) {
                qm->qp_base = HPRE_PF_DEF_Q_BASE;
-               qm->qp_num = hpre_pf_q_num;
+               qm->qp_num = pf_q_num;
+               qm->qm_list = &hpre_devices;
        }
-       qm->use_dma_api = true;
 
-       return 0;
+       return hisi_qm_init(qm);
 }
 
 static void hpre_log_hw_error(struct hisi_qm *qm, u32 err_sts)
@@ -693,8 +749,6 @@ static void hpre_log_hw_error(struct hisi_qm *qm, u32 err_sts)
                                 err->msg, err->int_msk);
                err++;
        }
-
-       writel(err_sts, qm->io_base + HPRE_HAC_SOURCE_INT);
 }
 
 static u32 hpre_get_hw_err_status(struct hisi_qm *qm)
@@ -702,16 +756,38 @@ static u32 hpre_get_hw_err_status(struct hisi_qm *qm)
        return readl(qm->io_base + HPRE_HAC_INT_STATUS);
 }
 
+static void hpre_clear_hw_err_status(struct hisi_qm *qm, u32 err_sts)
+{
+       writel(err_sts, qm->io_base + HPRE_HAC_SOURCE_INT);
+}
+
+static void hpre_open_axi_master_ooo(struct hisi_qm *qm)
+{
+       u32 value;
+
+       value = readl(qm->io_base + HPRE_AM_OOO_SHUTDOWN_ENB);
+       writel(value & ~HPRE_AM_OOO_SHUTDOWN_ENABLE,
+              HPRE_ADDR(qm, HPRE_AM_OOO_SHUTDOWN_ENB));
+       writel(value | HPRE_AM_OOO_SHUTDOWN_ENABLE,
+              HPRE_ADDR(qm, HPRE_AM_OOO_SHUTDOWN_ENB));
+}
+
 static const struct hisi_qm_err_ini hpre_err_ini = {
+       .hw_init                = hpre_set_user_domain_and_cache,
        .hw_err_enable          = hpre_hw_error_enable,
        .hw_err_disable         = hpre_hw_error_disable,
        .get_dev_hw_err_status  = hpre_get_hw_err_status,
+       .clear_dev_hw_err_status = hpre_clear_hw_err_status,
        .log_dev_hw_err         = hpre_log_hw_error,
+       .open_axi_master_ooo    = hpre_open_axi_master_ooo,
        .err_info               = {
                .ce                     = QM_BASE_CE,
                .nfe                    = QM_BASE_NFE | QM_ACC_DO_TASK_TIMEOUT,
                .fe                     = 0,
-               .msi                    = QM_DB_RANDOM_INVALID,
+               .ecc_2bits_mask         = HPRE_CORE_ECC_2BIT_ERR |
+                                         HPRE_OOO_ECC_2BIT_ERR,
+               .msi_wr_port            = HPRE_WR_MSI_PORT,
+               .acpi_rst               = "HRST",
        }
 };
 
@@ -722,7 +798,7 @@ static int hpre_pf_probe_init(struct hpre *hpre)
 
        qm->ctrl_qp_num = HPRE_QUEUE_NUM_V2;
 
-       ret = hpre_set_user_domain_and_cache(hpre);
+       ret = hpre_set_user_domain_and_cache(qm);
        if (ret)
                return ret;
 
@@ -732,6 +808,20 @@ static int hpre_pf_probe_init(struct hpre *hpre)
        return 0;
 }
 
+static int hpre_probe_init(struct hpre *hpre)
+{
+       struct hisi_qm *qm = &hpre->qm;
+       int ret;
+
+       if (qm->fun_type == QM_HW_PF) {
+               ret = hpre_pf_probe_init(hpre);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static int hpre_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct hisi_qm *qm;
@@ -742,26 +832,17 @@ static int hpre_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (!hpre)
                return -ENOMEM;
 
-       pci_set_drvdata(pdev, hpre);
-
        qm = &hpre->qm;
-       ret = hpre_qm_pre_init(qm, pdev);
-       if (ret)
-               return ret;
-
-       ret = hisi_qm_init(qm);
-       if (ret)
+       ret = hpre_qm_init(qm, pdev);
+       if (ret) {
+               pci_err(pdev, "Failed to init HPRE QM (%d)!\n", ret);
                return ret;
+       }
 
-       if (pdev->is_physfn) {
-               ret = hpre_pf_probe_init(hpre);
-               if (ret)
-                       goto err_with_qm_init;
-       } else if (qm->fun_type == QM_HW_VF && qm->ver == QM_HW_V2) {
-               /* v2 starts to support get vft by mailbox */
-               ret = hisi_qm_get_vft(qm, &qm->qp_base, &qm->qp_num);
-               if (ret)
-                       goto err_with_qm_init;
+       ret = hpre_probe_init(hpre);
+       if (ret) {
+               pci_err(pdev, "Failed to probe (%d)!\n", ret);
+               goto err_with_qm_init;
        }
 
        ret = hisi_qm_start(qm);
@@ -779,8 +860,18 @@ static int hpre_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                pci_err(pdev, "fail to register algs to crypto!\n");
                goto err_with_qm_start;
        }
+
+       if (qm->fun_type == QM_HW_PF && vfs_num) {
+               ret = hisi_qm_sriov_enable(pdev, vfs_num);
+               if (ret < 0)
+                       goto err_with_crypto_register;
+       }
+
        return 0;
 
+err_with_crypto_register:
+       hpre_algs_unregister();
+
 err_with_qm_start:
        hisi_qm_del_from_list(qm, &hpre_devices);
        hisi_qm_stop(qm);
@@ -794,107 +885,6 @@ err_with_qm_init:
        return ret;
 }
 
-static int hpre_vf_q_assign(struct hpre *hpre, int num_vfs)
-{
-       struct hisi_qm *qm = &hpre->qm;
-       u32 qp_num = qm->qp_num;
-       int q_num, remain_q_num, i;
-       u32 q_base = qp_num;
-       int ret;
-
-       if (!num_vfs)
-               return -EINVAL;
-
-       remain_q_num = qm->ctrl_qp_num - qp_num;
-
-       /* If remaining queues are not enough, return error. */
-       if (remain_q_num < num_vfs)
-               return -EINVAL;
-
-       q_num = remain_q_num / num_vfs;
-       for (i = 1; i <= num_vfs; i++) {
-               if (i == num_vfs)
-                       q_num += remain_q_num % num_vfs;
-               ret = hisi_qm_set_vft(qm, i, q_base, (u32)q_num);
-               if (ret)
-                       return ret;
-               q_base += q_num;
-       }
-
-       return 0;
-}
-
-static int hpre_clear_vft_config(struct hpre *hpre)
-{
-       struct hisi_qm *qm = &hpre->qm;
-       u32 num_vfs = hpre->num_vfs;
-       int ret;
-       u32 i;
-
-       for (i = 1; i <= num_vfs; i++) {
-               ret = hisi_qm_set_vft(qm, i, 0, 0);
-               if (ret)
-                       return ret;
-       }
-       hpre->num_vfs = 0;
-
-       return 0;
-}
-
-static int hpre_sriov_enable(struct pci_dev *pdev, int max_vfs)
-{
-       struct hpre *hpre = pci_get_drvdata(pdev);
-       int pre_existing_vfs, num_vfs, ret;
-
-       pre_existing_vfs = pci_num_vf(pdev);
-       if (pre_existing_vfs) {
-               pci_err(pdev,
-                       "Can't enable VF. Please disable pre-enabled VFs!\n");
-               return 0;
-       }
-
-       num_vfs = min_t(int, max_vfs, HPRE_VF_NUM);
-       ret = hpre_vf_q_assign(hpre, num_vfs);
-       if (ret) {
-               pci_err(pdev, "Can't assign queues for VF!\n");
-               return ret;
-       }
-
-       hpre->num_vfs = num_vfs;
-
-       ret = pci_enable_sriov(pdev, num_vfs);
-       if (ret) {
-               pci_err(pdev, "Can't enable VF!\n");
-               hpre_clear_vft_config(hpre);
-               return ret;
-       }
-
-       return num_vfs;
-}
-
-static int hpre_sriov_disable(struct pci_dev *pdev)
-{
-       struct hpre *hpre = pci_get_drvdata(pdev);
-
-       if (pci_vfs_assigned(pdev)) {
-               pci_err(pdev, "Failed to disable VFs while VFs are assigned!\n");
-               return -EPERM;
-       }
-
-       /* remove in hpre_pci_driver will be called to free VF resources */
-       pci_disable_sriov(pdev);
-
-       return hpre_clear_vft_config(hpre);
-}
-
-static int hpre_sriov_configure(struct pci_dev *pdev, int num_vfs)
-{
-       if (num_vfs)
-               return hpre_sriov_enable(pdev, num_vfs);
-       else
-               return hpre_sriov_disable(pdev);
-}
-
 static void hpre_remove(struct pci_dev *pdev)
 {
        struct hpre *hpre = pci_get_drvdata(pdev);
@@ -903,8 +893,8 @@ static void hpre_remove(struct pci_dev *pdev)
 
        hpre_algs_unregister();
        hisi_qm_del_from_list(qm, &hpre_devices);
-       if (qm->fun_type == QM_HW_PF && hpre->num_vfs != 0) {
-               ret = hpre_sriov_disable(pdev);
+       if (qm->fun_type == QM_HW_PF && qm->vfs_num) {
+               ret = hisi_qm_sriov_disable(pdev);
                if (ret) {
                        pci_err(pdev, "Disable SRIOV fail!\n");
                        return;
@@ -924,6 +914,9 @@ static void hpre_remove(struct pci_dev *pdev)
 
 static const struct pci_error_handlers hpre_err_handler = {
        .error_detected         = hisi_qm_dev_err_detected,
+       .slot_reset             = hisi_qm_dev_slot_reset,
+       .reset_prepare          = hisi_qm_reset_prepare,
+       .reset_done             = hisi_qm_reset_done,
 };
 
 static struct pci_driver hpre_pci_driver = {
@@ -931,7 +924,7 @@ static struct pci_driver hpre_pci_driver = {
        .id_table               = hpre_dev_ids,
        .probe                  = hpre_probe,
        .remove                 = hpre_remove,
-       .sriov_configure        = hpre_sriov_configure,
+       .sriov_configure        = hisi_qm_sriov_configure,
        .err_handler            = &hpre_err_handler,
 };
 
index f795fb557630ba1c232e7dec06a6408791977493..9bb263cec6c306796778b0f2a82f49b94176fffa 100644 (file)
@@ -1,9 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2019 HiSilicon Limited. */
 #include <asm/page.h>
+#include <linux/acpi.h>
+#include <linux/aer.h>
 #include <linux/bitmap.h>
 #include <linux/debugfs.h>
 #include <linux/dma-mapping.h>
+#include <linux/idr.h>
 #include <linux/io.h>
 #include <linux/irqreturn.h>
 #include <linux/log2.h>
@@ -53,6 +56,7 @@
 #define QM_SQ_TYPE_SHIFT               8
 
 #define QM_SQ_TYPE_MASK                        GENMASK(3, 0)
+#define QM_SQ_TAIL_IDX(sqc)            ((le16_to_cpu((sqc)->w11) >> 6) & 0x1)
 
 /* cqc shift */
 #define QM_CQ_HOP_NUM_SHIFT            0
@@ -64,6 +68,7 @@
 
 #define QM_CQE_PHASE(cqe)              (le16_to_cpu((cqe)->w7) & 0x1)
 #define QM_QC_CQE_SIZE                 4
+#define QM_CQ_TAIL_IDX(cqc)            ((le16_to_cpu((cqc)->w11) >> 6) & 0x1)
 
 /* eqc shift */
 #define QM_EQE_AEQE_SIZE               (2UL << 12)
 #define QM_DFX_CNT_CLR_CE              0x100118
 
 #define QM_ABNORMAL_INT_SOURCE         0x100000
+#define QM_ABNORMAL_INT_SOURCE_CLR     GENMASK(12, 0)
 #define QM_ABNORMAL_INT_MASK           0x100004
 #define QM_ABNORMAL_INT_MASK_VALUE     0x1fff
 #define QM_ABNORMAL_INT_STATUS         0x100008
+#define QM_ABNORMAL_INT_SET            0x10000c
 #define QM_ABNORMAL_INF00              0x100010
 #define QM_FIFO_OVERFLOW_TYPE          0xc0
 #define QM_FIFO_OVERFLOW_TYPE_SHIFT    6
 #define QM_RAS_CE_TIMES_PER_IRQ                1
 #define QM_RAS_MSI_INT_SEL             0x1040f4
 
+#define QM_DEV_RESET_FLAG              0
+#define QM_RESET_WAIT_TIMEOUT          400
+#define QM_PEH_VENDOR_ID               0x1000d8
+#define ACC_VENDOR_ID_VALUE            0x5a5a
+#define QM_PEH_DFX_INFO0               0x1000fc
+#define ACC_PEH_SRIOV_CTRL_VF_MSE_SHIFT        3
+#define ACC_PEH_MSI_DISABLE            GENMASK(31, 0)
+#define ACC_MASTER_GLOBAL_CTRL_SHUTDOWN        0x1
+#define ACC_MASTER_TRANS_RETURN_RW     3
+#define ACC_MASTER_TRANS_RETURN                0x300150
+#define ACC_MASTER_GLOBAL_CTRL         0x300000
+#define ACC_AM_CFG_PORT_WR_EN          0x30001c
+#define QM_RAS_NFE_MBIT_DISABLE                ~QM_ECC_MBIT
+#define ACC_AM_ROB_ECC_INT_STS         0x300104
+#define ACC_ROB_ECC_ERR_MULTPL         BIT(1)
+
+#define POLL_PERIOD                    10
+#define POLL_TIMEOUT                   1000
+#define WAIT_PERIOD_US_MAX             200
+#define WAIT_PERIOD_US_MIN             100
+#define MAX_WAIT_COUNTS                        1000
 #define QM_CACHE_WB_START              0x204
 #define QM_CACHE_WB_DONE               0x208
 
 #define QM_SQE_DATA_ALIGN_MASK         GENMASK(6, 0)
 #define QMC_ALIGN(sz)                  ALIGN(sz, 32)
 
+#define QM_DBG_READ_LEN                256
+#define QM_DBG_WRITE_LEN               1024
 #define QM_DBG_TMP_BUF_LEN             22
+#define QM_PCI_COMMAND_INVALID         ~0
+
+#define QM_SQE_ADDR_MASK               GENMASK(7, 0)
 
 #define QM_MK_CQC_DW3_V1(hop_num, pg_sz, buf_sz, cqe_sz) \
        (((hop_num) << QM_CQ_HOP_NUM_SHIFT)     | \
@@ -190,6 +223,12 @@ enum vft_type {
        CQC_VFT,
 };
 
+enum acc_err_result {
+       ACC_ERR_NONE,
+       ACC_ERR_NEED_RESET,
+       ACC_ERR_RECOVERED,
+};
+
 struct qm_cqe {
        __le32 rsvd0;
        __le16 cmd_id;
@@ -284,10 +323,22 @@ struct hisi_qm_hw_ops {
                      u8 cmd, u16 index, u8 priority);
        u32 (*get_irq_num)(struct hisi_qm *qm);
        int (*debug_init)(struct hisi_qm *qm);
-       void (*hw_error_init)(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe,
-                             u32 msi);
+       void (*hw_error_init)(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe);
        void (*hw_error_uninit)(struct hisi_qm *qm);
-       pci_ers_result_t (*hw_error_handle)(struct hisi_qm *qm);
+       enum acc_err_result (*hw_error_handle)(struct hisi_qm *qm);
+};
+
+struct qm_dfx_item {
+       const char *name;
+       u32 offset;
+};
+
+static struct qm_dfx_item qm_dfx_files[] = {
+       {"err_irq", offsetof(struct qm_dfx, err_irq_cnt)},
+       {"aeq_irq", offsetof(struct qm_dfx, aeq_irq_cnt)},
+       {"abnormal_irq", offsetof(struct qm_dfx, abnormal_irq_cnt)},
+       {"create_qp_err", offsetof(struct qm_dfx, create_qp_err_cnt)},
+       {"mb_err", offsetof(struct qm_dfx, mb_err_cnt)},
 };
 
 static const char * const qm_debug_file_name[] = {
@@ -325,6 +376,93 @@ static const char * const qm_fifo_overflow[] = {
        "cq", "eq", "aeq",
 };
 
+static const char * const qm_s[] = {
+       "init", "start", "close", "stop",
+};
+
+static const char * const qp_s[] = {
+       "none", "init", "start", "stop", "close",
+};
+
+static bool qm_avail_state(struct hisi_qm *qm, enum qm_state new)
+{
+       enum qm_state curr = atomic_read(&qm->status.flags);
+       bool avail = false;
+
+       switch (curr) {
+       case QM_INIT:
+               if (new == QM_START || new == QM_CLOSE)
+                       avail = true;
+               break;
+       case QM_START:
+               if (new == QM_STOP)
+                       avail = true;
+               break;
+       case QM_STOP:
+               if (new == QM_CLOSE || new == QM_START)
+                       avail = true;
+               break;
+       default:
+               break;
+       }
+
+       dev_dbg(&qm->pdev->dev, "change qm state from %s to %s\n",
+               qm_s[curr], qm_s[new]);
+
+       if (!avail)
+               dev_warn(&qm->pdev->dev, "Can not change qm state from %s to %s\n",
+                        qm_s[curr], qm_s[new]);
+
+       return avail;
+}
+
+static bool qm_qp_avail_state(struct hisi_qm *qm, struct hisi_qp *qp,
+                             enum qp_state new)
+{
+       enum qm_state qm_curr = atomic_read(&qm->status.flags);
+       enum qp_state qp_curr = 0;
+       bool avail = false;
+
+       if (qp)
+               qp_curr = atomic_read(&qp->qp_status.flags);
+
+       switch (new) {
+       case QP_INIT:
+               if (qm_curr == QM_START || qm_curr == QM_INIT)
+                       avail = true;
+               break;
+       case QP_START:
+               if ((qm_curr == QM_START && qp_curr == QP_INIT) ||
+                   (qm_curr == QM_START && qp_curr == QP_STOP))
+                       avail = true;
+               break;
+       case QP_STOP:
+               if ((qm_curr == QM_START && qp_curr == QP_START) ||
+                   (qp_curr == QP_INIT))
+                       avail = true;
+               break;
+       case QP_CLOSE:
+               if ((qm_curr == QM_START && qp_curr == QP_INIT) ||
+                   (qm_curr == QM_START && qp_curr == QP_STOP) ||
+                   (qm_curr == QM_STOP && qp_curr == QP_STOP)  ||
+                   (qm_curr == QM_STOP && qp_curr == QP_INIT))
+                       avail = true;
+               break;
+       default:
+               break;
+       }
+
+       dev_dbg(&qm->pdev->dev, "change qp state from %s to %s in QM %s\n",
+               qp_s[qp_curr], qp_s[new], qm_s[qm_curr]);
+
+       if (!avail)
+               dev_warn(&qm->pdev->dev,
+                        "Can not change qp state from %s to %s in QM %s\n",
+                        qp_s[qp_curr], qp_s[new], qm_s[qm_curr]);
+
+       return avail;
+}
+
 /* return 0 mailbox ready, -ETIMEDOUT hardware timeout */
 static int qm_wait_mb_ready(struct hisi_qm *qm)
 {
@@ -393,6 +531,8 @@ static int qm_mb(struct hisi_qm *qm, u8 cmd, dma_addr_t dma_addr, u16 queue,
 busy_unlock:
        mutex_unlock(&qm->mailbox_lock);
 
+       if (ret)
+               atomic64_inc(&qm->debug.dfx.mb_err_cnt);
        return ret;
 }
 
@@ -460,7 +600,7 @@ static struct hisi_qp *qm_to_hisi_qp(struct hisi_qm *qm, struct qm_eqe *eqe)
 {
        u16 cqn = le32_to_cpu(eqe->dw0) & QM_EQE_CQN_MASK;
 
-       return qm->qp_array[cqn];
+       return &qm->qp_array[cqn];
 }
 
 static void qm_cq_head_update(struct hisi_qp *qp)
@@ -510,8 +650,7 @@ static void qm_work_process(struct work_struct *work)
        while (QM_EQE_PHASE(eqe) == qm->status.eqc_phase) {
                eqe_num++;
                qp = qm_to_hisi_qp(qm, eqe);
-               if (qp)
-                       qm_poll_qp(qp, qm);
+               qm_poll_qp(qp, qm);
 
                if (qm->status.eq_head == QM_Q_DEPTH - 1) {
                        qm->status.eqc_phase = !qm->status.eqc_phase;
@@ -551,6 +690,7 @@ static irqreturn_t qm_irq(int irq, void *data)
        if (readl(qm->io_base + QM_VF_EQ_INT_SOURCE))
                return do_qm_irq(irq, data);
 
+       atomic64_inc(&qm->debug.dfx.err_irq_cnt);
        dev_err(&qm->pdev->dev, "invalid int source\n");
        qm_db(qm, 0, QM_DOORBELL_CMD_EQ, qm->status.eq_head, 0);
 
@@ -563,6 +703,7 @@ static irqreturn_t qm_aeq_irq(int irq, void *data)
        struct qm_aeqe *aeqe = qm->aeqe + qm->status.aeq_head;
        u32 type;
 
+       atomic64_inc(&qm->debug.dfx.aeq_irq_cnt);
        if (!readl(qm->io_base + QM_VF_AEQ_INT_SOURCE))
                return IRQ_NONE;
 
@@ -590,79 +731,20 @@ static irqreturn_t qm_aeq_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static irqreturn_t qm_abnormal_irq(int irq, void *data)
-{
-       const struct hisi_qm_hw_error *err = qm_hw_error;
-       struct hisi_qm *qm = data;
-       struct device *dev = &qm->pdev->dev;
-       u32 error_status, tmp;
-
-       /* read err sts */
-       tmp = readl(qm->io_base + QM_ABNORMAL_INT_STATUS);
-       error_status = qm->msi_mask & tmp;
-
-       while (err->msg) {
-               if (err->int_msk & error_status)
-                       dev_err(dev, "%s [error status=0x%x] found\n",
-                               err->msg, err->int_msk);
-
-               err++;
-       }
-
-       /* clear err sts */
-       writel(error_status, qm->io_base + QM_ABNORMAL_INT_SOURCE);
-
-       return IRQ_HANDLED;
-}
-
-static int qm_irq_register(struct hisi_qm *qm)
-{
-       struct pci_dev *pdev = qm->pdev;
-       int ret;
-
-       ret = request_irq(pci_irq_vector(pdev, QM_EQ_EVENT_IRQ_VECTOR),
-                         qm_irq, IRQF_SHARED, qm->dev_name, qm);
-       if (ret)
-               return ret;
-
-       if (qm->ver == QM_HW_V2) {
-               ret = request_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR),
-                                 qm_aeq_irq, IRQF_SHARED, qm->dev_name, qm);
-               if (ret)
-                       goto err_aeq_irq;
-
-               if (qm->fun_type == QM_HW_PF) {
-                       ret = request_irq(pci_irq_vector(pdev,
-                                         QM_ABNORMAL_EVENT_IRQ_VECTOR),
-                                         qm_abnormal_irq, IRQF_SHARED,
-                                         qm->dev_name, qm);
-                       if (ret)
-                               goto err_abonormal_irq;
-               }
-       }
-
-       return 0;
-
-err_abonormal_irq:
-       free_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR), qm);
-err_aeq_irq:
-       free_irq(pci_irq_vector(pdev, QM_EQ_EVENT_IRQ_VECTOR), qm);
-       return ret;
-}
-
 static void qm_irq_unregister(struct hisi_qm *qm)
 {
        struct pci_dev *pdev = qm->pdev;
 
        free_irq(pci_irq_vector(pdev, QM_EQ_EVENT_IRQ_VECTOR), qm);
 
-       if (qm->ver == QM_HW_V2) {
-               free_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR), qm);
+       if (qm->ver == QM_HW_V1)
+               return;
+
+       free_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR), qm);
 
-               if (qm->fun_type == QM_HW_PF)
-                       free_irq(pci_irq_vector(pdev,
-                                QM_ABNORMAL_EVENT_IRQ_VECTOR), qm);
-       }
+       if (qm->fun_type == QM_HW_PF)
+               free_irq(pci_irq_vector(pdev,
+                        QM_ABNORMAL_EVENT_IRQ_VECTOR), qm);
 }
 
 static void qm_init_qp_status(struct hisi_qp *qp)
@@ -672,7 +754,7 @@ static void qm_init_qp_status(struct hisi_qp *qp)
        qp_status->sq_tail = 0;
        qp_status->cq_head = 0;
        qp_status->cqc_phase = true;
-       qp_status->flags = 0;
+       atomic_set(&qp_status->flags, 0);
 }
 
 static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
@@ -683,36 +765,26 @@ static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
        if (number > 0) {
                switch (type) {
                case SQC_VFT:
-                       switch (qm->ver) {
-                       case QM_HW_V1:
+                       if (qm->ver == QM_HW_V1) {
                                tmp = QM_SQC_VFT_BUF_SIZE       |
                                      QM_SQC_VFT_SQC_SIZE       |
                                      QM_SQC_VFT_INDEX_NUMBER   |
                                      QM_SQC_VFT_VALID          |
                                      (u64)base << QM_SQC_VFT_START_SQN_SHIFT;
-                               break;
-                       case QM_HW_V2:
+                       } else {
                                tmp = (u64)base << QM_SQC_VFT_START_SQN_SHIFT |
                                      QM_SQC_VFT_VALID |
                                      (u64)(number - 1) << QM_SQC_VFT_SQN_SHIFT;
-                               break;
-                       case QM_HW_UNKNOWN:
-                               break;
                        }
                        break;
                case CQC_VFT:
-                       switch (qm->ver) {
-                       case QM_HW_V1:
+                       if (qm->ver == QM_HW_V1) {
                                tmp = QM_CQC_VFT_BUF_SIZE       |
                                      QM_CQC_VFT_SQC_SIZE       |
                                      QM_CQC_VFT_INDEX_NUMBER   |
                                      QM_CQC_VFT_VALID;
-                               break;
-                       case QM_HW_V2:
+                       } else {
                                tmp = QM_CQC_VFT_VALID;
-                               break;
-                       case QM_HW_UNKNOWN:
-                               break;
                        }
                        break;
                }
@@ -986,1380 +1058,2893 @@ static const struct file_operations qm_regs_fops = {
        .release = single_release,
 };
 
-static int qm_create_debugfs_file(struct hisi_qm *qm, enum qm_debug_file index)
+static ssize_t qm_cmd_read(struct file *filp, char __user *buffer,
+                          size_t count, loff_t *pos)
 {
-       struct dentry *qm_d = qm->debug.qm_d;
-       struct debugfs_file *file = qm->debug.files + index;
+       char buf[QM_DBG_READ_LEN];
+       int len;
 
-       debugfs_create_file(qm_debug_file_name[index], 0600, qm_d, file,
-                           &qm_debug_fops);
+       if (*pos)
+               return 0;
 
-       file->index = index;
-       mutex_init(&file->lock);
-       file->debug = &qm->debug;
+       if (count < QM_DBG_READ_LEN)
+               return -ENOSPC;
 
-       return 0;
-}
+       len = snprintf(buf, QM_DBG_READ_LEN, "%s\n",
+                      "Please echo help to cmd to get help information");
 
-static void qm_hw_error_init_v1(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe,
-                               u32 msi)
-{
-       writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK);
+       if (copy_to_user(buffer, buf, len))
+               return -EFAULT;
+
+       return (*pos = len);
 }
 
-static void qm_hw_error_init_v2(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe,
-                               u32 msi)
+static void *qm_ctx_alloc(struct hisi_qm *qm, size_t ctx_size,
+                         dma_addr_t *dma_addr)
 {
-       u32 irq_enable = ce | nfe | fe | msi;
-       u32 irq_unmask = ~irq_enable;
-
-       qm->error_mask = ce | nfe | fe;
-       qm->msi_mask = msi;
+       struct device *dev = &qm->pdev->dev;
+       void *ctx_addr;
 
-       /* configure error type */
-       writel(ce, qm->io_base + QM_RAS_CE_ENABLE);
-       writel(QM_RAS_CE_TIMES_PER_IRQ, qm->io_base + QM_RAS_CE_THRESHOLD);
-       writel(nfe, qm->io_base + QM_RAS_NFE_ENABLE);
-       writel(fe, qm->io_base + QM_RAS_FE_ENABLE);
+       ctx_addr = kzalloc(ctx_size, GFP_KERNEL);
+       if (!ctx_addr)
+               return ERR_PTR(-ENOMEM);
 
-       /* use RAS irq default, so only set QM_RAS_MSI_INT_SEL for MSI */
-       writel(msi, qm->io_base + QM_RAS_MSI_INT_SEL);
+       *dma_addr = dma_map_single(dev, ctx_addr, ctx_size, DMA_FROM_DEVICE);
+       if (dma_mapping_error(dev, *dma_addr)) {
+               dev_err(dev, "DMA mapping error!\n");
+               kfree(ctx_addr);
+               return ERR_PTR(-ENOMEM);
+       }
 
-       irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
-       writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK);
+       return ctx_addr;
 }
 
-static void qm_hw_error_uninit_v2(struct hisi_qm *qm)
+static void qm_ctx_free(struct hisi_qm *qm, size_t ctx_size,
+                       const void *ctx_addr, dma_addr_t *dma_addr)
 {
-       writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK);
+       struct device *dev = &qm->pdev->dev;
+
+       dma_unmap_single(dev, *dma_addr, ctx_size, DMA_FROM_DEVICE);
+       kfree(ctx_addr);
 }
 
-static void qm_log_hw_error(struct hisi_qm *qm, u32 error_status)
+static int dump_show(struct hisi_qm *qm, void *info,
+                    unsigned int info_size, char *info_name)
 {
-       const struct hisi_qm_hw_error *err;
        struct device *dev = &qm->pdev->dev;
-       u32 reg_val, type, vf_num;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(qm_hw_error); i++) {
-               err = &qm_hw_error[i];
-               if (!(err->int_msk & error_status))
-                       continue;
+       u8 *info_buf, *info_curr = info;
+       u32 i;
+#define BYTE_PER_DW    4
 
-               dev_err(dev, "%s [error status=0x%x] found\n",
-                       err->msg, err->int_msk);
+       info_buf = kzalloc(info_size, GFP_KERNEL);
+       if (!info_buf)
+               return -ENOMEM;
 
-               if (err->int_msk & QM_DB_TIMEOUT) {
-                       reg_val = readl(qm->io_base + QM_ABNORMAL_INF01);
-                       type = (reg_val & QM_DB_TIMEOUT_TYPE) >>
-                              QM_DB_TIMEOUT_TYPE_SHIFT;
-                       vf_num = reg_val & QM_DB_TIMEOUT_VF;
-                       dev_err(dev, "qm %s doorbell timeout in function %u\n",
-                               qm_db_timeout[type], vf_num);
-               } else if (err->int_msk & QM_OF_FIFO_OF) {
-                       reg_val = readl(qm->io_base + QM_ABNORMAL_INF00);
-                       type = (reg_val & QM_FIFO_OVERFLOW_TYPE) >>
-                              QM_FIFO_OVERFLOW_TYPE_SHIFT;
-                       vf_num = reg_val & QM_FIFO_OVERFLOW_VF;
+       for (i = 0; i < info_size; i++, info_curr++) {
+               if (i % BYTE_PER_DW == 0)
+                       info_buf[i + 3UL] = *info_curr;
+               else if (i % BYTE_PER_DW == 1)
+                       info_buf[i + 1UL] = *info_curr;
+               else if (i % BYTE_PER_DW == 2)
+                       info_buf[i - 1] = *info_curr;
+               else if (i % BYTE_PER_DW == 3)
+                       info_buf[i - 3] = *info_curr;
+       }
 
-                       if (type < ARRAY_SIZE(qm_fifo_overflow))
-                               dev_err(dev, "qm %s fifo overflow in function %u\n",
-                                       qm_fifo_overflow[type], vf_num);
-                       else
-                               dev_err(dev, "unknown error type\n");
-               }
+       dev_info(dev, "%s DUMP\n", info_name);
+       for (i = 0; i < info_size; i += BYTE_PER_DW) {
+               pr_info("DW%d: %02X%02X %02X%02X\n", i / BYTE_PER_DW,
+                       info_buf[i], info_buf[i + 1UL],
+                       info_buf[i + 2UL], info_buf[i + 3UL]);
        }
+
+       kfree(info_buf);
+
+       return 0;
 }
 
-static pci_ers_result_t qm_hw_error_handle_v2(struct hisi_qm *qm)
+static int qm_dump_sqc_raw(struct hisi_qm *qm, dma_addr_t dma_addr, u16 qp_id)
 {
-       u32 error_status, tmp;
+       return qm_mb(qm, QM_MB_CMD_SQC, dma_addr, qp_id, 1);
+}
 
-       /* read err sts */
-       tmp = readl(qm->io_base + QM_ABNORMAL_INT_STATUS);
-       error_status = qm->error_mask & tmp;
+static int qm_dump_cqc_raw(struct hisi_qm *qm, dma_addr_t dma_addr, u16 qp_id)
+{
+       return qm_mb(qm, QM_MB_CMD_CQC, dma_addr, qp_id, 1);
+}
 
-       if (error_status) {
-               qm_log_hw_error(qm, error_status);
+static int qm_sqc_dump(struct hisi_qm *qm, const char *s)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct qm_sqc *sqc, *sqc_curr;
+       dma_addr_t sqc_dma;
+       u32 qp_id;
+       int ret;
 
-               /* clear err sts */
-               writel(error_status, qm->io_base + QM_ABNORMAL_INT_SOURCE);
+       if (!s)
+               return -EINVAL;
 
-               return PCI_ERS_RESULT_NEED_RESET;
+       ret = kstrtou32(s, 0, &qp_id);
+       if (ret || qp_id >= qm->qp_num) {
+               dev_err(dev, "Please input qp num (0-%d)", qm->qp_num - 1);
+               return -EINVAL;
        }
 
-       return PCI_ERS_RESULT_RECOVERED;
-}
+       sqc = qm_ctx_alloc(qm, sizeof(*sqc), &sqc_dma);
+       if (IS_ERR(sqc))
+               return PTR_ERR(sqc);
 
-static const struct hisi_qm_hw_ops qm_hw_ops_v1 = {
-       .qm_db = qm_db_v1,
-       .get_irq_num = qm_get_irq_num_v1,
-       .hw_error_init = qm_hw_error_init_v1,
-};
+       ret = qm_dump_sqc_raw(qm, sqc_dma, qp_id);
+       if (ret) {
+               down_read(&qm->qps_lock);
+               if (qm->sqc) {
+                       sqc_curr = qm->sqc + qp_id;
 
-static const struct hisi_qm_hw_ops qm_hw_ops_v2 = {
-       .get_vft = qm_get_vft_v2,
-       .qm_db = qm_db_v2,
-       .get_irq_num = qm_get_irq_num_v2,
-       .hw_error_init = qm_hw_error_init_v2,
-       .hw_error_uninit = qm_hw_error_uninit_v2,
-       .hw_error_handle = qm_hw_error_handle_v2,
-};
+                       ret = dump_show(qm, sqc_curr, sizeof(*sqc),
+                                       "SOFT SQC");
+                       if (ret)
+                               dev_info(dev, "Show soft sqc failed!\n");
+               }
+               up_read(&qm->qps_lock);
 
-static void *qm_get_avail_sqe(struct hisi_qp *qp)
-{
-       struct hisi_qp_status *qp_status = &qp->qp_status;
-       u16 sq_tail = qp_status->sq_tail;
+               goto err_free_ctx;
+       }
 
-       if (unlikely(atomic_read(&qp->qp_status.used) == QM_Q_DEPTH))
-               return NULL;
+       ret = dump_show(qm, sqc, sizeof(*sqc), "SQC");
+       if (ret)
+               dev_info(dev, "Show hw sqc failed!\n");
 
-       return qp->sqe + sq_tail * qp->qm->sqe_size;
+err_free_ctx:
+       qm_ctx_free(qm, sizeof(*sqc), sqc, &sqc_dma);
+       return ret;
 }
 
-/**
- * hisi_qm_create_qp() - Create a queue pair from qm.
- * @qm: The qm we create a qp from.
- * @alg_type: Accelerator specific algorithm type in sqc.
- *
- * return created qp, -EBUSY if all qps in qm allocated, -ENOMEM if allocating
- * qp memory fails.
- */
-struct hisi_qp *hisi_qm_create_qp(struct hisi_qm *qm, u8 alg_type)
+static int qm_cqc_dump(struct hisi_qm *qm, const char *s)
 {
        struct device *dev = &qm->pdev->dev;
-       struct hisi_qp *qp;
-       int qp_id, ret;
-
-       qp = kzalloc(sizeof(*qp), GFP_KERNEL);
-       if (!qp)
-               return ERR_PTR(-ENOMEM);
+       struct qm_cqc *cqc, *cqc_curr;
+       dma_addr_t cqc_dma;
+       u32 qp_id;
+       int ret;
 
-       write_lock(&qm->qps_lock);
+       if (!s)
+               return -EINVAL;
 
-       qp_id = find_first_zero_bit(qm->qp_bitmap, qm->qp_num);
-       if (qp_id >= qm->qp_num) {
-               write_unlock(&qm->qps_lock);
-               dev_info(&qm->pdev->dev, "QM all queues are busy!\n");
-               ret = -EBUSY;
-               goto err_free_qp;
+       ret = kstrtou32(s, 0, &qp_id);
+       if (ret || qp_id >= qm->qp_num) {
+               dev_err(dev, "Please input qp num (0-%d)", qm->qp_num - 1);
+               return -EINVAL;
        }
-       set_bit(qp_id, qm->qp_bitmap);
-       qm->qp_array[qp_id] = qp;
-       qm->qp_in_used++;
 
-       write_unlock(&qm->qps_lock);
+       cqc = qm_ctx_alloc(qm, sizeof(*cqc), &cqc_dma);
+       if (IS_ERR(cqc))
+               return PTR_ERR(cqc);
 
-       qp->qm = qm;
+       ret = qm_dump_cqc_raw(qm, cqc_dma, qp_id);
+       if (ret) {
+               down_read(&qm->qps_lock);
+               if (qm->cqc) {
+                       cqc_curr = qm->cqc + qp_id;
 
-       if (qm->use_dma_api) {
-               qp->qdma.size = qm->sqe_size * QM_Q_DEPTH +
-                               sizeof(struct qm_cqe) * QM_Q_DEPTH;
-               qp->qdma.va = dma_alloc_coherent(dev, qp->qdma.size,
-                                                &qp->qdma.dma, GFP_KERNEL);
-               if (!qp->qdma.va) {
-                       ret = -ENOMEM;
-                       goto err_clear_bit;
+                       ret = dump_show(qm, cqc_curr, sizeof(*cqc),
+                                       "SOFT CQC");
+                       if (ret)
+                               dev_info(dev, "Show soft cqc failed!\n");
                }
+               up_read(&qm->qps_lock);
 
-               dev_dbg(dev, "allocate qp dma buf(va=%pK, dma=%pad, size=%zx)\n",
-                       qp->qdma.va, &qp->qdma.dma, qp->qdma.size);
+               goto err_free_ctx;
        }
 
-       qp->qp_id = qp_id;
-       qp->alg_type = alg_type;
-
-       return qp;
+       ret = dump_show(qm, cqc, sizeof(*cqc), "CQC");
+       if (ret)
+               dev_info(dev, "Show hw cqc failed!\n");
 
-err_clear_bit:
-       write_lock(&qm->qps_lock);
-       qm->qp_array[qp_id] = NULL;
-       clear_bit(qp_id, qm->qp_bitmap);
-       write_unlock(&qm->qps_lock);
-err_free_qp:
-       kfree(qp);
-       return ERR_PTR(ret);
+err_free_ctx:
+       qm_ctx_free(qm, sizeof(*cqc), cqc, &cqc_dma);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_create_qp);
 
-/**
- * hisi_qm_release_qp() - Release a qp back to its qm.
- * @qp: The qp we want to release.
- *
- * This function releases the resource of a qp.
- */
-void hisi_qm_release_qp(struct hisi_qp *qp)
+static int qm_eqc_aeqc_dump(struct hisi_qm *qm, char *s, size_t size,
+                           int cmd, char *name)
 {
-       struct hisi_qm *qm = qp->qm;
-       struct qm_dma *qdma = &qp->qdma;
        struct device *dev = &qm->pdev->dev;
+       dma_addr_t xeqc_dma;
+       void *xeqc;
+       int ret;
 
-       if (qm->use_dma_api && qdma->va)
-               dma_free_coherent(dev, qdma->size, qdma->va, qdma->dma);
+       if (strsep(&s, " ")) {
+               dev_err(dev, "Please do not input extra characters!\n");
+               return -EINVAL;
+       }
 
-       write_lock(&qm->qps_lock);
-       qm->qp_array[qp->qp_id] = NULL;
-       clear_bit(qp->qp_id, qm->qp_bitmap);
-       qm->qp_in_used--;
-       write_unlock(&qm->qps_lock);
+       xeqc = qm_ctx_alloc(qm, size, &xeqc_dma);
+       if (IS_ERR(xeqc))
+               return PTR_ERR(xeqc);
 
-       kfree(qp);
-}
-EXPORT_SYMBOL_GPL(hisi_qm_release_qp);
+       ret = qm_mb(qm, cmd, xeqc_dma, 0, 1);
+       if (ret)
+               goto err_free_ctx;
 
-static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, int pasid)
+       ret = dump_show(qm, xeqc, size, name);
+       if (ret)
+               dev_info(dev, "Show hw %s failed!\n", name);
+
+err_free_ctx:
+       qm_ctx_free(qm, size, xeqc, &xeqc_dma);
+       return ret;
+}
+
+static int q_dump_param_parse(struct hisi_qm *qm, char *s,
+                             u32 *e_id, u32 *q_id)
 {
-       struct hisi_qm *qm = qp->qm;
        struct device *dev = &qm->pdev->dev;
-       enum qm_hw_ver ver = qm->ver;
-       struct qm_sqc *sqc;
-       struct qm_cqc *cqc;
-       dma_addr_t sqc_dma;
-       dma_addr_t cqc_dma;
+       unsigned int qp_num = qm->qp_num;
+       char *presult;
        int ret;
 
-       qm_init_qp_status(qp);
+       presult = strsep(&s, " ");
+       if (!presult) {
+               dev_err(dev, "Please input qp number!\n");
+               return -EINVAL;
+       }
 
-       sqc = kzalloc(sizeof(struct qm_sqc), GFP_KERNEL);
-       if (!sqc)
-               return -ENOMEM;
-       sqc_dma = dma_map_single(dev, sqc, sizeof(struct qm_sqc),
-                                DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, sqc_dma)) {
-               kfree(sqc);
-               return -ENOMEM;
+       ret = kstrtou32(presult, 0, q_id);
+       if (ret || *q_id >= qp_num) {
+               dev_err(dev, "Please input qp num (0-%d)", qp_num - 1);
+               return -EINVAL;
        }
 
-       INIT_QC_COMMON(sqc, qp->sqe_dma, pasid);
-       if (ver == QM_HW_V1) {
-               sqc->dw3 = cpu_to_le32(QM_MK_SQC_DW3_V1(0, 0, 0, qm->sqe_size));
-               sqc->w8 = cpu_to_le16(QM_Q_DEPTH - 1);
-       } else if (ver == QM_HW_V2) {
-               sqc->dw3 = cpu_to_le32(QM_MK_SQC_DW3_V2(qm->sqe_size));
-               sqc->w8 = 0; /* rand_qc */
+       presult = strsep(&s, " ");
+       if (!presult) {
+               dev_err(dev, "Please input sqe number!\n");
+               return -EINVAL;
        }
-       sqc->cq_num = cpu_to_le16(qp_id);
-       sqc->w13 = cpu_to_le16(QM_MK_SQC_W13(0, 1, qp->alg_type));
 
-       ret = qm_mb(qm, QM_MB_CMD_SQC, sqc_dma, qp_id, 0);
-       dma_unmap_single(dev, sqc_dma, sizeof(struct qm_sqc), DMA_TO_DEVICE);
-       kfree(sqc);
+       ret = kstrtou32(presult, 0, e_id);
+       if (ret || *e_id >= QM_Q_DEPTH) {
+               dev_err(dev, "Please input sqe num (0-%d)", QM_Q_DEPTH - 1);
+               return -EINVAL;
+       }
+
+       if (strsep(&s, " ")) {
+               dev_err(dev, "Please do not input extra characters!\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int qm_sq_dump(struct hisi_qm *qm, char *s)
+{
+       struct device *dev = &qm->pdev->dev;
+       void *sqe, *sqe_curr;
+       struct hisi_qp *qp;
+       u32 qp_id, sqe_id;
+       int ret;
+
+       ret = q_dump_param_parse(qm, s, &sqe_id, &qp_id);
        if (ret)
                return ret;
 
-       cqc = kzalloc(sizeof(struct qm_cqc), GFP_KERNEL);
-       if (!cqc)
-               return -ENOMEM;
-       cqc_dma = dma_map_single(dev, cqc, sizeof(struct qm_cqc),
-                                DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, cqc_dma)) {
-               kfree(cqc);
+       sqe = kzalloc(qm->sqe_size * QM_Q_DEPTH, GFP_KERNEL);
+       if (!sqe)
                return -ENOMEM;
-       }
 
-       INIT_QC_COMMON(cqc, qp->cqe_dma, pasid);
-       if (ver == QM_HW_V1) {
-               cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V1(0, 0, 0, 4));
-               cqc->w8 = cpu_to_le16(QM_Q_DEPTH - 1);
-       } else if (ver == QM_HW_V2) {
-               cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V2(4));
-               cqc->w8 = 0;
-       }
-       cqc->dw6 = cpu_to_le32(1 << QM_CQ_PHASE_SHIFT | 1 << QM_CQ_FLAG_SHIFT);
+       qp = &qm->qp_array[qp_id];
+       memcpy(sqe, qp->sqe, qm->sqe_size * QM_Q_DEPTH);
+       sqe_curr = sqe + (u32)(sqe_id * qm->sqe_size);
+       memset(sqe_curr + qm->debug.sqe_mask_offset, QM_SQE_ADDR_MASK,
+              qm->debug.sqe_mask_len);
 
-       ret = qm_mb(qm, QM_MB_CMD_CQC, cqc_dma, qp_id, 0);
-       dma_unmap_single(dev, cqc_dma, sizeof(struct qm_cqc), DMA_TO_DEVICE);
-       kfree(cqc);
+       ret = dump_show(qm, sqe_curr, qm->sqe_size, "SQE");
+       if (ret)
+               dev_info(dev, "Show sqe failed!\n");
+
+       kfree(sqe);
 
        return ret;
 }
 
-/**
- * hisi_qm_start_qp() - Start a qp into running.
- * @qp: The qp we want to start to run.
- * @arg: Accelerator specific argument.
- *
- * After this function, qp can receive request from user. Return 0 if
- * successful, Return -EBUSY if failed.
- */
-int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg)
+static int qm_cq_dump(struct hisi_qm *qm, char *s)
 {
-       struct hisi_qm *qm = qp->qm;
        struct device *dev = &qm->pdev->dev;
-       enum qm_hw_ver ver = qm->ver;
-       int qp_id = qp->qp_id;
-       int pasid = arg;
-       size_t off = 0;
+       struct qm_cqe *cqe_curr;
+       struct hisi_qp *qp;
+       u32 qp_id, cqe_id;
        int ret;
 
-#define QP_INIT_BUF(qp, type, size) do { \
-       (qp)->type = ((qp)->qdma.va + (off)); \
-       (qp)->type##_dma = (qp)->qdma.dma + (off); \
-       off += (size); \
-} while (0)
+       ret = q_dump_param_parse(qm, s, &cqe_id, &qp_id);
+       if (ret)
+               return ret;
+
+       qp = &qm->qp_array[qp_id];
+       cqe_curr = qp->cqe + cqe_id;
+       ret = dump_show(qm, cqe_curr, sizeof(struct qm_cqe), "CQE");
+       if (ret)
+               dev_info(dev, "Show cqe failed!\n");
+
+       return ret;
+}
+
+static int qm_eq_aeq_dump(struct hisi_qm *qm, const char *s,
+                         size_t size, char *name)
+{
+       struct device *dev = &qm->pdev->dev;
+       void *xeqe;
+       u32 xeqe_id;
+       int ret;
 
-       if (!qp->qdma.dma) {
-               dev_err(dev, "cannot get qm dma buffer\n");
+       if (!s)
                return -EINVAL;
-       }
 
-       /* sq need 128 bytes alignment */
-       if (qp->qdma.dma & QM_SQE_DATA_ALIGN_MASK) {
-               dev_err(dev, "qm sq is not aligned to 128 byte\n");
+       ret = kstrtou32(s, 0, &xeqe_id);
+       if (ret || xeqe_id >= QM_Q_DEPTH) {
+               dev_err(dev, "Please input aeqe num (0-%d)", QM_Q_DEPTH - 1);
                return -EINVAL;
        }
 
-       QP_INIT_BUF(qp, sqe, qm->sqe_size * QM_Q_DEPTH);
-       QP_INIT_BUF(qp, cqe, sizeof(struct qm_cqe) * QM_Q_DEPTH);
+       down_read(&qm->qps_lock);
 
-       dev_dbg(dev, "init qp buffer(v%d):\n"
-                    " sqe      (%pK, %lx)\n"
-                    " cqe      (%pK, %lx)\n",
-                    ver, qp->sqe, (unsigned long)qp->sqe_dma,
-                    qp->cqe, (unsigned long)qp->cqe_dma);
+       if (qm->eqe && !strcmp(name, "EQE")) {
+               xeqe = qm->eqe + xeqe_id;
+       } else if (qm->aeqe && !strcmp(name, "AEQE")) {
+               xeqe = qm->aeqe + xeqe_id;
+       } else {
+               ret = -EINVAL;
+               goto err_unlock;
+       }
 
-       ret = qm_qp_ctx_cfg(qp, qp_id, pasid);
+       ret = dump_show(qm, xeqe, size, name);
        if (ret)
-               return ret;
+               dev_info(dev, "Show %s failed!\n", name);
 
-       dev_dbg(dev, "queue %d started\n", qp_id);
-
-       return 0;
+err_unlock:
+       up_read(&qm->qps_lock);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_start_qp);
 
-/**
- * hisi_qm_stop_qp() - Stop a qp in qm.
- * @qp: The qp we want to stop.
- *
- * This function is reverse of hisi_qm_start_qp. Return 0 if successful.
- */
-int hisi_qm_stop_qp(struct hisi_qp *qp)
+static int qm_dbg_help(struct hisi_qm *qm, char *s)
 {
-       struct device *dev = &qp->qm->pdev->dev;
-       int i = 0;
-
-       /* it is stopped */
-       if (test_bit(QP_STOP, &qp->qp_status.flags))
-               return 0;
+       struct device *dev = &qm->pdev->dev;
 
-       while (atomic_read(&qp->qp_status.used)) {
-               i++;
-               msleep(20);
-               if (i == 10) {
-                       dev_err(dev, "Cannot drain out data for stopping, Force to stop!\n");
-                       return 0;
-               }
+       if (strsep(&s, " ")) {
+               dev_err(dev, "Please do not input extra characters!\n");
+               return -EINVAL;
        }
 
-       set_bit(QP_STOP, &qp->qp_status.flags);
-
-       dev_dbg(dev, "stop queue %u!", qp->qp_id);
+       dev_info(dev, "available commands:\n");
+       dev_info(dev, "sqc <num>\n");
+       dev_info(dev, "cqc <num>\n");
+       dev_info(dev, "eqc\n");
+       dev_info(dev, "aeqc\n");
+       dev_info(dev, "sq <num> <e>\n");
+       dev_info(dev, "cq <num> <e>\n");
+       dev_info(dev, "eq <e>\n");
+       dev_info(dev, "aeq <e>\n");
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_stop_qp);
 
-/**
- * hisi_qp_send() - Queue up a task in the hardware queue.
- * @qp: The qp in which to put the message.
- * @msg: The message.
- *
- * This function will return -EBUSY if qp is currently full, and -EAGAIN
- * if qp related qm is resetting.
- */
-int hisi_qp_send(struct hisi_qp *qp, const void *msg)
+static int qm_cmd_write_dump(struct hisi_qm *qm, const char *cmd_buf)
 {
-       struct hisi_qp_status *qp_status = &qp->qp_status;
-       u16 sq_tail = qp_status->sq_tail;
-       u16 sq_tail_next = (sq_tail + 1) % QM_Q_DEPTH;
-       void *sqe = qm_get_avail_sqe(qp);
+       struct device *dev = &qm->pdev->dev;
+       char *presult, *s;
+       int ret;
 
-       if (unlikely(test_bit(QP_STOP, &qp->qp_status.flags))) {
-               dev_info(&qp->qm->pdev->dev, "QP is stopped or resetting\n");
-               return -EAGAIN;
+       s = kstrdup(cmd_buf, GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       presult = strsep(&s, " ");
+       if (!presult) {
+               kfree(s);
+               return -EINVAL;
        }
 
-       if (!sqe)
-               return -EBUSY;
+       if (!strcmp(presult, "sqc"))
+               ret = qm_sqc_dump(qm, s);
+       else if (!strcmp(presult, "cqc"))
+               ret = qm_cqc_dump(qm, s);
+       else if (!strcmp(presult, "eqc"))
+               ret = qm_eqc_aeqc_dump(qm, s, sizeof(struct qm_eqc),
+                                      QM_MB_CMD_EQC, "EQC");
+       else if (!strcmp(presult, "aeqc"))
+               ret = qm_eqc_aeqc_dump(qm, s, sizeof(struct qm_aeqc),
+                                      QM_MB_CMD_AEQC, "AEQC");
+       else if (!strcmp(presult, "sq"))
+               ret = qm_sq_dump(qm, s);
+       else if (!strcmp(presult, "cq"))
+               ret = qm_cq_dump(qm, s);
+       else if (!strcmp(presult, "eq"))
+               ret = qm_eq_aeq_dump(qm, s, sizeof(struct qm_eqe), "EQE");
+       else if (!strcmp(presult, "aeq"))
+               ret = qm_eq_aeq_dump(qm, s, sizeof(struct qm_aeqe), "AEQE");
+       else if (!strcmp(presult, "help"))
+               ret = qm_dbg_help(qm, s);
+       else
+               ret = -EINVAL;
 
-       memcpy(sqe, msg, qp->qm->sqe_size);
+       if (ret)
+               dev_info(dev, "Please echo help\n");
 
-       qm_db(qp->qm, qp->qp_id, QM_DOORBELL_CMD_SQ, sq_tail_next, 0);
-       atomic_inc(&qp->qp_status.used);
-       qp_status->sq_tail = sq_tail_next;
+       kfree(s);
 
-       return 0;
+       return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qp_send);
 
-static void hisi_qm_cache_wb(struct hisi_qm *qm)
+static ssize_t qm_cmd_write(struct file *filp, const char __user *buffer,
+                           size_t count, loff_t *pos)
 {
-       unsigned int val;
+       struct hisi_qm *qm = filp->private_data;
+       char *cmd_buf, *cmd_buf_tmp;
+       int ret;
+
+       if (*pos)
+               return 0;
+
+       /* Judge if the instance is being reset. */
+       if (unlikely(atomic_read(&qm->status.flags) == QM_STOP))
+               return 0;
+
+       if (count > QM_DBG_WRITE_LEN)
+               return -ENOSPC;
+
+       cmd_buf = kzalloc(count + 1, GFP_KERNEL);
+       if (!cmd_buf)
+               return -ENOMEM;
 
-       if (qm->ver == QM_HW_V2) {
-               writel(0x1, qm->io_base + QM_CACHE_WB_START);
-               if (readl_relaxed_poll_timeout(qm->io_base + QM_CACHE_WB_DONE,
-                                              val, val & BIT(0), 10, 1000))
-                       dev_err(&qm->pdev->dev, "QM writeback sqc cache fail!\n");
+       if (copy_from_user(cmd_buf, buffer, count)) {
+               kfree(cmd_buf);
+               return -EFAULT;
        }
-}
 
-static void qm_qp_event_notifier(struct hisi_qp *qp)
-{
-       wake_up_interruptible(&qp->uacce_q->wait);
-}
+       cmd_buf[count] = '\0';
 
-static int hisi_qm_get_available_instances(struct uacce_device *uacce)
-{
-       int i, ret;
-       struct hisi_qm *qm = uacce->priv;
+       cmd_buf_tmp = strchr(cmd_buf, '\n');
+       if (cmd_buf_tmp) {
+               *cmd_buf_tmp = '\0';
+               count = cmd_buf_tmp - cmd_buf + 1;
+       }
 
-       read_lock(&qm->qps_lock);
-       for (i = 0, ret = 0; i < qm->qp_num; i++)
-               if (!qm->qp_array[i])
-                       ret++;
-       read_unlock(&qm->qps_lock);
+       ret = qm_cmd_write_dump(qm, cmd_buf);
+       if (ret) {
+               kfree(cmd_buf);
+               return ret;
+       }
 
-       return ret;
+       kfree(cmd_buf);
+
+       return count;
 }
 
-static int hisi_qm_uacce_get_queue(struct uacce_device *uacce,
-                                  unsigned long arg,
-                                  struct uacce_queue *q)
+static const struct file_operations qm_cmd_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = qm_cmd_read,
+       .write = qm_cmd_write,
+};
+
+static int qm_create_debugfs_file(struct hisi_qm *qm, enum qm_debug_file index)
 {
-       struct hisi_qm *qm = uacce->priv;
-       struct hisi_qp *qp;
-       u8 alg_type = 0;
+       struct dentry *qm_d = qm->debug.qm_d;
+       struct debugfs_file *file = qm->debug.files + index;
 
-       qp = hisi_qm_create_qp(qm, alg_type);
-       if (IS_ERR(qp))
-               return PTR_ERR(qp);
+       debugfs_create_file(qm_debug_file_name[index], 0600, qm_d, file,
+                           &qm_debug_fops);
 
-       q->priv = qp;
-       q->uacce = uacce;
-       qp->uacce_q = q;
-       qp->event_cb = qm_qp_event_notifier;
-       qp->pasid = arg;
+       file->index = index;
+       mutex_init(&file->lock);
+       file->debug = &qm->debug;
 
        return 0;
 }
 
-static void hisi_qm_uacce_put_queue(struct uacce_queue *q)
+static void qm_hw_error_init_v1(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe)
 {
-       struct hisi_qp *qp = q->priv;
-
-       hisi_qm_cache_wb(qp->qm);
-       hisi_qm_release_qp(qp);
+       writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK);
 }
 
-/* map sq/cq/doorbell to user space */
-static int hisi_qm_uacce_mmap(struct uacce_queue *q,
-                             struct vm_area_struct *vma,
-                             struct uacce_qfile_region *qfr)
+static void qm_hw_error_init_v2(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe)
 {
-       struct hisi_qp *qp = q->priv;
-       struct hisi_qm *qm = qp->qm;
-       size_t sz = vma->vm_end - vma->vm_start;
-       struct pci_dev *pdev = qm->pdev;
-       struct device *dev = &pdev->dev;
-       unsigned long vm_pgoff;
-       int ret;
+       u32 irq_enable = ce | nfe | fe;
+       u32 irq_unmask = ~irq_enable;
 
-       switch (qfr->type) {
-       case UACCE_QFRT_MMIO:
-               if (qm->ver == QM_HW_V2) {
-                       if (sz > PAGE_SIZE * (QM_DOORBELL_PAGE_NR +
-                           QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE))
-                               return -EINVAL;
-               } else {
-                       if (sz > PAGE_SIZE * QM_DOORBELL_PAGE_NR)
-                               return -EINVAL;
-               }
+       qm->error_mask = ce | nfe | fe;
 
-               vma->vm_flags |= VM_IO;
+       /* clear QM hw residual error source */
+       writel(QM_ABNORMAL_INT_SOURCE_CLR,
+              qm->io_base + QM_ABNORMAL_INT_SOURCE);
 
-               return remap_pfn_range(vma, vma->vm_start,
-                                      qm->phys_base >> PAGE_SHIFT,
-                                      sz, pgprot_noncached(vma->vm_page_prot));
-       case UACCE_QFRT_DUS:
-               if (sz != qp->qdma.size)
-                       return -EINVAL;
+       /* configure error type */
+       writel(ce, qm->io_base + QM_RAS_CE_ENABLE);
+       writel(QM_RAS_CE_TIMES_PER_IRQ, qm->io_base + QM_RAS_CE_THRESHOLD);
+       writel(nfe, qm->io_base + QM_RAS_NFE_ENABLE);
+       writel(fe, qm->io_base + QM_RAS_FE_ENABLE);
 
-               /*
-                * dma_mmap_coherent() requires vm_pgoff as 0
-                * restore vm_pfoff to initial value for mmap()
-                */
-               vm_pgoff = vma->vm_pgoff;
-               vma->vm_pgoff = 0;
-               ret = dma_mmap_coherent(dev, vma, qp->qdma.va,
-                                       qp->qdma.dma, sz);
-               vma->vm_pgoff = vm_pgoff;
-               return ret;
-
-       default:
-               return -EINVAL;
-       }
+       irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
+       writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK);
 }
 
-static int hisi_qm_uacce_start_queue(struct uacce_queue *q)
+static void qm_hw_error_uninit_v2(struct hisi_qm *qm)
 {
-       struct hisi_qp *qp = q->priv;
-
-       return hisi_qm_start_qp(qp, qp->pasid);
+       writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK);
 }
 
-static void hisi_qm_uacce_stop_queue(struct uacce_queue *q)
+static void qm_log_hw_error(struct hisi_qm *qm, u32 error_status)
 {
-       hisi_qm_stop_qp(q->priv);
-}
+       const struct hisi_qm_hw_error *err;
+       struct device *dev = &qm->pdev->dev;
+       u32 reg_val, type, vf_num;
+       int i;
 
-static int qm_set_sqctype(struct uacce_queue *q, u16 type)
-{
-       struct hisi_qm *qm = q->uacce->priv;
-       struct hisi_qp *qp = q->priv;
+       for (i = 0; i < ARRAY_SIZE(qm_hw_error); i++) {
+               err = &qm_hw_error[i];
+               if (!(err->int_msk & error_status))
+                       continue;
 
-       write_lock(&qm->qps_lock);
-       qp->alg_type = type;
-       write_unlock(&qm->qps_lock);
+               dev_err(dev, "%s [error status=0x%x] found\n",
+                       err->msg, err->int_msk);
 
-       return 0;
+               if (err->int_msk & QM_DB_TIMEOUT) {
+                       reg_val = readl(qm->io_base + QM_ABNORMAL_INF01);
+                       type = (reg_val & QM_DB_TIMEOUT_TYPE) >>
+                              QM_DB_TIMEOUT_TYPE_SHIFT;
+                       vf_num = reg_val & QM_DB_TIMEOUT_VF;
+                       dev_err(dev, "qm %s doorbell timeout in function %u\n",
+                               qm_db_timeout[type], vf_num);
+               } else if (err->int_msk & QM_OF_FIFO_OF) {
+                       reg_val = readl(qm->io_base + QM_ABNORMAL_INF00);
+                       type = (reg_val & QM_FIFO_OVERFLOW_TYPE) >>
+                              QM_FIFO_OVERFLOW_TYPE_SHIFT;
+                       vf_num = reg_val & QM_FIFO_OVERFLOW_VF;
+
+                       if (type < ARRAY_SIZE(qm_fifo_overflow))
+                               dev_err(dev, "qm %s fifo overflow in function %u\n",
+                                       qm_fifo_overflow[type], vf_num);
+                       else
+                               dev_err(dev, "unknown error type\n");
+               }
+       }
 }
 
-static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd,
-                               unsigned long arg)
+static enum acc_err_result qm_hw_error_handle_v2(struct hisi_qm *qm)
 {
-       struct hisi_qp *qp = q->priv;
-       struct hisi_qp_ctx qp_ctx;
+       u32 error_status, tmp;
 
-       if (cmd == UACCE_CMD_QM_SET_QP_CTX) {
-               if (copy_from_user(&qp_ctx, (void __user *)arg,
-                                  sizeof(struct hisi_qp_ctx)))
-                       return -EFAULT;
+       /* read err sts */
+       tmp = readl(qm->io_base + QM_ABNORMAL_INT_STATUS);
+       error_status = qm->error_mask & tmp;
 
-               if (qp_ctx.qc_type != 0 && qp_ctx.qc_type != 1)
-                       return -EINVAL;
+       if (error_status) {
+               if (error_status & QM_ECC_MBIT)
+                       qm->err_status.is_qm_ecc_mbit = true;
 
-               qm_set_sqctype(q, qp_ctx.qc_type);
-               qp_ctx.id = qp->qp_id;
+               qm_log_hw_error(qm, error_status);
+               if (error_status == QM_DB_RANDOM_INVALID) {
+                       writel(error_status, qm->io_base +
+                              QM_ABNORMAL_INT_SOURCE);
+                       return ACC_ERR_RECOVERED;
+               }
 
-               if (copy_to_user((void __user *)arg, &qp_ctx,
-                                sizeof(struct hisi_qp_ctx)))
-                       return -EFAULT;
-       } else {
-               return -EINVAL;
+               return ACC_ERR_NEED_RESET;
        }
 
-       return 0;
+       return ACC_ERR_RECOVERED;
 }
 
-static const struct uacce_ops uacce_qm_ops = {
-       .get_available_instances = hisi_qm_get_available_instances,
-       .get_queue = hisi_qm_uacce_get_queue,
-       .put_queue = hisi_qm_uacce_put_queue,
-       .start_queue = hisi_qm_uacce_start_queue,
-       .stop_queue = hisi_qm_uacce_stop_queue,
-       .mmap = hisi_qm_uacce_mmap,
-       .ioctl = hisi_qm_uacce_ioctl,
+static const struct hisi_qm_hw_ops qm_hw_ops_v1 = {
+       .qm_db = qm_db_v1,
+       .get_irq_num = qm_get_irq_num_v1,
+       .hw_error_init = qm_hw_error_init_v1,
 };
 
-static int qm_alloc_uacce(struct hisi_qm *qm)
+static const struct hisi_qm_hw_ops qm_hw_ops_v2 = {
+       .get_vft = qm_get_vft_v2,
+       .qm_db = qm_db_v2,
+       .get_irq_num = qm_get_irq_num_v2,
+       .hw_error_init = qm_hw_error_init_v2,
+       .hw_error_uninit = qm_hw_error_uninit_v2,
+       .hw_error_handle = qm_hw_error_handle_v2,
+};
+
+static void *qm_get_avail_sqe(struct hisi_qp *qp)
 {
-       struct pci_dev *pdev = qm->pdev;
-       struct uacce_device *uacce;
-       unsigned long mmio_page_nr;
-       unsigned long dus_page_nr;
-       struct uacce_interface interface = {
-               .flags = UACCE_DEV_SVA,
-               .ops = &uacce_qm_ops,
-       };
+       struct hisi_qp_status *qp_status = &qp->qp_status;
+       u16 sq_tail = qp_status->sq_tail;
 
-       strncpy(interface.name, pdev->driver->name, sizeof(interface.name));
+       if (unlikely(atomic_read(&qp->qp_status.used) == QM_Q_DEPTH))
+               return NULL;
 
-       uacce = uacce_alloc(&pdev->dev, &interface);
-       if (IS_ERR(uacce))
-               return PTR_ERR(uacce);
+       return qp->sqe + sq_tail * qp->qm->sqe_size;
+}
 
-       if (uacce->flags & UACCE_DEV_SVA) {
-               qm->use_sva = true;
-       } else {
-               /* only consider sva case */
-               uacce_remove(uacce);
-               qm->uacce = NULL;
-               return -EINVAL;
-       }
+static struct hisi_qp *qm_create_qp_nolock(struct hisi_qm *qm, u8 alg_type)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct hisi_qp *qp;
+       int qp_id;
 
-       uacce->is_vf = pdev->is_virtfn;
-       uacce->priv = qm;
-       uacce->algs = qm->algs;
+       if (!qm_qp_avail_state(qm, NULL, QP_INIT))
+               return ERR_PTR(-EPERM);
 
-       if (qm->ver == QM_HW_V1) {
-               mmio_page_nr = QM_DOORBELL_PAGE_NR;
-               uacce->api_ver = HISI_QM_API_VER_BASE;
-       } else {
-               mmio_page_nr = QM_DOORBELL_PAGE_NR +
-                       QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE;
-               uacce->api_ver = HISI_QM_API_VER2_BASE;
+       if (qm->qp_in_used == qm->qp_num) {
+               dev_info_ratelimited(dev, "All %u queues of QM are busy!\n",
+                                    qm->qp_num);
+               atomic64_inc(&qm->debug.dfx.create_qp_err_cnt);
+               return ERR_PTR(-EBUSY);
        }
 
-       dus_page_nr = (PAGE_SIZE - 1 + qm->sqe_size * QM_Q_DEPTH +
-                      sizeof(struct qm_cqe) * QM_Q_DEPTH) >> PAGE_SHIFT;
+       qp_id = idr_alloc_cyclic(&qm->qp_idr, NULL, 0, qm->qp_num, GFP_ATOMIC);
+       if (qp_id < 0) {
+               dev_info_ratelimited(dev, "All %u queues of QM are busy!\n",
+                                   qm->qp_num);
+               atomic64_inc(&qm->debug.dfx.create_qp_err_cnt);
+               return ERR_PTR(-EBUSY);
+       }
 
-       uacce->qf_pg_num[UACCE_QFRT_MMIO] = mmio_page_nr;
-       uacce->qf_pg_num[UACCE_QFRT_DUS]  = dus_page_nr;
+       qp = &qm->qp_array[qp_id];
 
-       qm->uacce = uacce;
+       memset(qp->cqe, 0, sizeof(struct qm_cqe) * QM_Q_DEPTH);
 
-       return 0;
+       qp->event_cb = NULL;
+       qp->req_cb = NULL;
+       qp->qp_id = qp_id;
+       qp->alg_type = alg_type;
+       qm->qp_in_used++;
+       atomic_set(&qp->qp_status.flags, QP_INIT);
+
+       return qp;
 }
 
 /**
- * hisi_qm_get_free_qp_num() - Get free number of qp in qm.
- * @qm: The qm which want to get free qp.
+ * hisi_qm_create_qp() - Create a queue pair from qm.
+ * @qm: The qm we create a qp from.
+ * @alg_type: Accelerator specific algorithm type in sqc.
  *
- * This function return free number of qp in qm.
+ * return created qp, -EBUSY if all qps in qm allocated, -ENOMEM if allocating
+ * qp memory fails.
  */
-int hisi_qm_get_free_qp_num(struct hisi_qm *qm)
+struct hisi_qp *hisi_qm_create_qp(struct hisi_qm *qm, u8 alg_type)
 {
-       int ret;
+       struct hisi_qp *qp;
 
-       read_lock(&qm->qps_lock);
-       ret = qm->qp_num - qm->qp_in_used;
-       read_unlock(&qm->qps_lock);
+       down_write(&qm->qps_lock);
+       qp = qm_create_qp_nolock(qm, alg_type);
+       up_write(&qm->qps_lock);
 
-       return ret;
+       return qp;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_get_free_qp_num);
+EXPORT_SYMBOL_GPL(hisi_qm_create_qp);
 
 /**
- * hisi_qm_init() - Initialize configures about qm.
- * @qm: The qm needing init.
+ * hisi_qm_release_qp() - Release a qp back to its qm.
+ * @qp: The qp we want to release.
  *
- * This function init qm, then we can call hisi_qm_start to put qm into work.
+ * This function releases the resource of a qp.
  */
-int hisi_qm_init(struct hisi_qm *qm)
+void hisi_qm_release_qp(struct hisi_qp *qp)
 {
-       struct pci_dev *pdev = qm->pdev;
-       struct device *dev = &pdev->dev;
-       unsigned int num_vec;
-       int ret;
+       struct hisi_qm *qm = qp->qm;
 
-       switch (qm->ver) {
-       case QM_HW_V1:
-               qm->ops = &qm_hw_ops_v1;
-               break;
-       case QM_HW_V2:
-               qm->ops = &qm_hw_ops_v2;
-               break;
-       default:
-               return -EINVAL;
+       down_write(&qm->qps_lock);
+
+       if (!qm_qp_avail_state(qm, qp, QP_CLOSE)) {
+               up_write(&qm->qps_lock);
+               return;
        }
 
-       ret = qm_alloc_uacce(qm);
-       if (ret < 0)
-               dev_warn(&pdev->dev, "fail to alloc uacce (%d)\n", ret);
+       qm->qp_in_used--;
+       idr_remove(&qm->qp_idr, qp->qp_id);
 
-       ret = pci_enable_device_mem(pdev);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Failed to enable device mem!\n");
-               goto err_remove_uacce;
-       }
+       up_write(&qm->qps_lock);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_release_qp);
 
-       ret = pci_request_mem_regions(pdev, qm->dev_name);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "Failed to request mem regions!\n");
-               goto err_disable_pcidev;
+static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, int pasid)
+{
+       struct hisi_qm *qm = qp->qm;
+       struct device *dev = &qm->pdev->dev;
+       enum qm_hw_ver ver = qm->ver;
+       struct qm_sqc *sqc;
+       struct qm_cqc *cqc;
+       dma_addr_t sqc_dma;
+       dma_addr_t cqc_dma;
+       int ret;
+
+       qm_init_qp_status(qp);
+
+       sqc = kzalloc(sizeof(struct qm_sqc), GFP_KERNEL);
+       if (!sqc)
+               return -ENOMEM;
+       sqc_dma = dma_map_single(dev, sqc, sizeof(struct qm_sqc),
+                                DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, sqc_dma)) {
+               kfree(sqc);
+               return -ENOMEM;
        }
 
-       qm->phys_base = pci_resource_start(pdev, PCI_BAR_2);
-       qm->phys_size = pci_resource_len(qm->pdev, PCI_BAR_2);
-       qm->io_base = ioremap(qm->phys_base, qm->phys_size);
-       if (!qm->io_base) {
-               ret = -EIO;
-               goto err_release_mem_regions;
+       INIT_QC_COMMON(sqc, qp->sqe_dma, pasid);
+       if (ver == QM_HW_V1) {
+               sqc->dw3 = cpu_to_le32(QM_MK_SQC_DW3_V1(0, 0, 0, qm->sqe_size));
+               sqc->w8 = cpu_to_le16(QM_Q_DEPTH - 1);
+       } else {
+               sqc->dw3 = cpu_to_le32(QM_MK_SQC_DW3_V2(qm->sqe_size));
+               sqc->w8 = 0; /* rand_qc */
        }
+       sqc->cq_num = cpu_to_le16(qp_id);
+       sqc->w13 = cpu_to_le16(QM_MK_SQC_W13(0, 1, qp->alg_type));
 
-       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
-       if (ret < 0)
-               goto err_iounmap;
-       pci_set_master(pdev);
+       ret = qm_mb(qm, QM_MB_CMD_SQC, sqc_dma, qp_id, 0);
+       dma_unmap_single(dev, sqc_dma, sizeof(struct qm_sqc), DMA_TO_DEVICE);
+       kfree(sqc);
+       if (ret)
+               return ret;
 
-       if (!qm->ops->get_irq_num) {
-               ret = -EOPNOTSUPP;
-               goto err_iounmap;
-       }
-       num_vec = qm->ops->get_irq_num(qm);
-       ret = pci_alloc_irq_vectors(pdev, num_vec, num_vec, PCI_IRQ_MSI);
-       if (ret < 0) {
-               dev_err(dev, "Failed to enable MSI vectors!\n");
-               goto err_iounmap;
+       cqc = kzalloc(sizeof(struct qm_cqc), GFP_KERNEL);
+       if (!cqc)
+               return -ENOMEM;
+       cqc_dma = dma_map_single(dev, cqc, sizeof(struct qm_cqc),
+                                DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, cqc_dma)) {
+               kfree(cqc);
+               return -ENOMEM;
        }
 
-       ret = qm_irq_register(qm);
-       if (ret)
-               goto err_free_irq_vectors;
-
-       qm->qp_in_used = 0;
-       mutex_init(&qm->mailbox_lock);
-       rwlock_init(&qm->qps_lock);
-       INIT_WORK(&qm->work, qm_work_process);
-
-       dev_dbg(dev, "init qm %s with %s\n", pdev->is_physfn ? "pf" : "vf",
-               qm->use_dma_api ? "dma api" : "iommu api");
-
-       return 0;
+       INIT_QC_COMMON(cqc, qp->cqe_dma, pasid);
+       if (ver == QM_HW_V1) {
+               cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V1(0, 0, 0, 4));
+               cqc->w8 = cpu_to_le16(QM_Q_DEPTH - 1);
+       } else {
+               cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V2(4));
+               cqc->w8 = 0;
+       }
+       cqc->dw6 = cpu_to_le32(1 << QM_CQ_PHASE_SHIFT | 1 << QM_CQ_FLAG_SHIFT);
 
-err_free_irq_vectors:
-       pci_free_irq_vectors(pdev);
-err_iounmap:
-       iounmap(qm->io_base);
-err_release_mem_regions:
-       pci_release_mem_regions(pdev);
-err_disable_pcidev:
-       pci_disable_device(pdev);
-err_remove_uacce:
-       uacce_remove(qm->uacce);
-       qm->uacce = NULL;
+       ret = qm_mb(qm, QM_MB_CMD_CQC, cqc_dma, qp_id, 0);
+       dma_unmap_single(dev, cqc_dma, sizeof(struct qm_cqc), DMA_TO_DEVICE);
+       kfree(cqc);
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_init);
 
-/**
- * hisi_qm_uninit() - Uninitialize qm.
- * @qm: The qm needed uninit.
- *
- * This function uninits qm related device resources.
- */
-void hisi_qm_uninit(struct hisi_qm *qm)
+static int qm_start_qp_nolock(struct hisi_qp *qp, unsigned long arg)
 {
-       struct pci_dev *pdev = qm->pdev;
-       struct device *dev = &pdev->dev;
+       struct hisi_qm *qm = qp->qm;
+       struct device *dev = &qm->pdev->dev;
+       int qp_id = qp->qp_id;
+       int pasid = arg;
+       int ret;
 
-       uacce_remove(qm->uacce);
-       qm->uacce = NULL;
+       if (!qm_qp_avail_state(qm, qp, QP_START))
+               return -EPERM;
 
-       if (qm->use_dma_api && qm->qdma.va) {
-               hisi_qm_cache_wb(qm);
-               dma_free_coherent(dev, qm->qdma.size,
-                                 qm->qdma.va, qm->qdma.dma);
-               memset(&qm->qdma, 0, sizeof(qm->qdma));
-       }
+       ret = qm_qp_ctx_cfg(qp, qp_id, pasid);
+       if (ret)
+               return ret;
 
-       qm_irq_unregister(qm);
-       pci_free_irq_vectors(pdev);
-       iounmap(qm->io_base);
-       pci_release_mem_regions(pdev);
-       pci_disable_device(pdev);
+       atomic_set(&qp->qp_status.flags, QP_START);
+       dev_dbg(dev, "queue %d started\n", qp_id);
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_uninit);
 
 /**
- * hisi_qm_get_vft() - Get vft from a qm.
- * @qm: The qm we want to get its vft.
- * @base: The base number of queue in vft.
- * @number: The number of queues in vft.
- *
- * We can allocate multiple queues to a qm by configuring virtual function
- * table. We get related configures by this function. Normally, we call this
- * function in VF driver to get the queue information.
+ * hisi_qm_start_qp() - Start a qp into running.
+ * @qp: The qp we want to start to run.
+ * @arg: Accelerator specific argument.
  *
- * qm hw v1 does not support this interface.
+ * After this function, qp can receive request from user. Return 0 if
+ * successful, Return -EBUSY if failed.
  */
-int hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number)
+int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg)
 {
-       if (!base || !number)
-               return -EINVAL;
+       struct hisi_qm *qm = qp->qm;
+       int ret;
 
-       if (!qm->ops->get_vft) {
-               dev_err(&qm->pdev->dev, "Don't support vft read!\n");
-               return -EINVAL;
-       }
+       down_write(&qm->qps_lock);
+       ret = qm_start_qp_nolock(qp, arg);
+       up_write(&qm->qps_lock);
 
-       return qm->ops->get_vft(qm, base, number);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_get_vft);
+EXPORT_SYMBOL_GPL(hisi_qm_start_qp);
 
 /**
- * hisi_qm_set_vft() - Set "virtual function table" for a qm.
- * @fun_num: Number of operated function.
- * @qm: The qm in which to set vft, alway in a PF.
- * @base: The base number of queue in vft.
- * @number: The number of queues in vft. 0 means invalid vft.
- *
- * This function is alway called in PF driver, it is used to assign queues
- * among PF and VFs.
- *
- * Assign queues A~B to PF: hisi_qm_set_vft(qm, 0, A, B - A + 1)
- * Assign queues A~B to VF: hisi_qm_set_vft(qm, 2, A, B - A + 1)
- * (VF function number 0x2)
+ * Determine whether the queue is cleared by judging the tail pointers of
+ * sq and cq.
  */
-int hisi_qm_set_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
-                   u32 number)
-{
-       u32 max_q_num = qm->ctrl_qp_num;
-
-       if (base >= max_q_num || number > max_q_num ||
-           (base + number) > max_q_num)
-               return -EINVAL;
-
-       return qm_set_sqc_cqc_vft(qm, fun_num, base, number);
-}
-EXPORT_SYMBOL_GPL(hisi_qm_set_vft);
-
-static void qm_init_eq_aeq_status(struct hisi_qm *qm)
-{
-       struct hisi_qm_status *status = &qm->status;
-
-       status->eq_head = 0;
-       status->aeq_head = 0;
-       status->eqc_phase = true;
-       status->aeqc_phase = true;
-}
-
-static int qm_eq_ctx_cfg(struct hisi_qm *qm)
+static int qm_drain_qp(struct hisi_qp *qp)
 {
+       size_t size = sizeof(struct qm_sqc) + sizeof(struct qm_cqc);
+       struct hisi_qm *qm = qp->qm;
        struct device *dev = &qm->pdev->dev;
-       struct qm_eqc *eqc;
-       struct qm_aeqc *aeqc;
-       dma_addr_t eqc_dma;
-       dma_addr_t aeqc_dma;
-       int ret;
+       struct qm_sqc *sqc;
+       struct qm_cqc *cqc;
+       dma_addr_t dma_addr;
+       int ret = 0, i = 0;
+       void *addr;
 
-       qm_init_eq_aeq_status(qm);
+       /*
+        * No need to judge if ECC multi-bit error occurs because the
+        * master OOO will be blocked.
+        */
+       if (qm->err_status.is_qm_ecc_mbit || qm->err_status.is_dev_ecc_mbit)
+               return 0;
 
-       eqc = kzalloc(sizeof(struct qm_eqc), GFP_KERNEL);
-       if (!eqc)
-               return -ENOMEM;
-       eqc_dma = dma_map_single(dev, eqc, sizeof(struct qm_eqc),
-                                DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, eqc_dma)) {
-               kfree(eqc);
+       addr = qm_ctx_alloc(qm, size, &dma_addr);
+       if (IS_ERR(addr)) {
+               dev_err(dev, "Failed to alloc ctx for sqc and cqc!\n");
                return -ENOMEM;
        }
 
-       eqc->base_l = cpu_to_le32(lower_32_bits(qm->eqe_dma));
-       eqc->base_h = cpu_to_le32(upper_32_bits(qm->eqe_dma));
-       if (qm->ver == QM_HW_V1)
-               eqc->dw3 = cpu_to_le32(QM_EQE_AEQE_SIZE);
-       eqc->dw6 = cpu_to_le32((QM_Q_DEPTH - 1) | (1 << QM_EQC_PHASE_SHIFT));
-       ret = qm_mb(qm, QM_MB_CMD_EQC, eqc_dma, 0, 0);
-       dma_unmap_single(dev, eqc_dma, sizeof(struct qm_eqc), DMA_TO_DEVICE);
-       kfree(eqc);
-       if (ret)
-               return ret;
+       while (++i) {
+               ret = qm_dump_sqc_raw(qm, dma_addr, qp->qp_id);
+               if (ret) {
+                       dev_err_ratelimited(dev, "Failed to dump sqc!\n");
+                       break;
+               }
+               sqc = addr;
 
-       aeqc = kzalloc(sizeof(struct qm_aeqc), GFP_KERNEL);
-       if (!aeqc)
-               return -ENOMEM;
-       aeqc_dma = dma_map_single(dev, aeqc, sizeof(struct qm_aeqc),
-                                 DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, aeqc_dma)) {
-               kfree(aeqc);
-               return -ENOMEM;
-       }
+               ret = qm_dump_cqc_raw(qm, (dma_addr + sizeof(struct qm_sqc)),
+                                     qp->qp_id);
+               if (ret) {
+                       dev_err_ratelimited(dev, "Failed to dump cqc!\n");
+                       break;
+               }
+               cqc = addr + sizeof(struct qm_sqc);
 
-       aeqc->base_l = cpu_to_le32(lower_32_bits(qm->aeqe_dma));
-       aeqc->base_h = cpu_to_le32(upper_32_bits(qm->aeqe_dma));
-       aeqc->dw6 = cpu_to_le32((QM_Q_DEPTH - 1) | (1 << QM_EQC_PHASE_SHIFT));
+               if ((sqc->tail == cqc->tail) &&
+                   (QM_SQ_TAIL_IDX(sqc) == QM_CQ_TAIL_IDX(cqc)))
+                       break;
 
-       ret = qm_mb(qm, QM_MB_CMD_AEQC, aeqc_dma, 0, 0);
-       dma_unmap_single(dev, aeqc_dma, sizeof(struct qm_aeqc), DMA_TO_DEVICE);
-       kfree(aeqc);
+               if (i == MAX_WAIT_COUNTS) {
+                       dev_err(dev, "Fail to empty queue %u!\n", qp->qp_id);
+                       ret = -EBUSY;
+                       break;
+               }
+
+               usleep_range(WAIT_PERIOD_US_MIN, WAIT_PERIOD_US_MAX);
+       }
+
+       qm_ctx_free(qm, size, addr, &dma_addr);
 
        return ret;
 }
 
-static int __hisi_qm_start(struct hisi_qm *qm)
+static int qm_stop_qp_nolock(struct hisi_qp *qp)
 {
-       struct pci_dev *pdev = qm->pdev;
-       struct device *dev = &pdev->dev;
-       size_t off = 0;
+       struct device *dev = &qp->qm->pdev->dev;
        int ret;
 
-#define QM_INIT_BUF(qm, type, num) do { \
-       (qm)->type = ((qm)->qdma.va + (off)); \
-       (qm)->type##_dma = (qm)->qdma.dma + (off); \
-       off += QMC_ALIGN(sizeof(struct qm_##type) * (num)); \
-} while (0)
-
-       WARN_ON(!qm->qdma.dma);
-
-       if (qm->qp_num == 0)
-               return -EINVAL;
-
-       if (qm->fun_type == QM_HW_PF) {
-               ret = qm_dev_mem_reset(qm);
-               if (ret)
-                       return ret;
-
-               ret = hisi_qm_set_vft(qm, 0, qm->qp_base, qm->qp_num);
-               if (ret)
-                       return ret;
+       /*
+        * It is allowed to stop and release qp when reset, If the qp is
+        * stopped when reset but still want to be released then, the
+        * is_resetting flag should be set negative so that this qp will not
+        * be restarted after reset.
+        */
+       if (atomic_read(&qp->qp_status.flags) == QP_STOP) {
+               qp->is_resetting = false;
+               return 0;
        }
 
-       QM_INIT_BUF(qm, eqe, QM_Q_DEPTH);
-       QM_INIT_BUF(qm, aeqe, QM_Q_DEPTH);
-       QM_INIT_BUF(qm, sqc, qm->qp_num);
-       QM_INIT_BUF(qm, cqc, qm->qp_num);
-
-       dev_dbg(dev, "init qm buffer:\n"
-                    " eqe      (%pK, %lx)\n"
-                    " aeqe     (%pK, %lx)\n"
-                    " sqc      (%pK, %lx)\n"
-                    " cqc      (%pK, %lx)\n",
-                    qm->eqe, (unsigned long)qm->eqe_dma,
-                    qm->aeqe, (unsigned long)qm->aeqe_dma,
-                    qm->sqc, (unsigned long)qm->sqc_dma,
-                    qm->cqc, (unsigned long)qm->cqc_dma);
+       if (!qm_qp_avail_state(qp->qm, qp, QP_STOP))
+               return -EPERM;
 
-       ret = qm_eq_ctx_cfg(qm);
-       if (ret)
-               return ret;
+       atomic_set(&qp->qp_status.flags, QP_STOP);
 
-       ret = qm_mb(qm, QM_MB_CMD_SQC_BT, qm->sqc_dma, 0, 0);
+       ret = qm_drain_qp(qp);
        if (ret)
-               return ret;
+               dev_err(dev, "Failed to drain out data for stopping!\n");
 
-       ret = qm_mb(qm, QM_MB_CMD_CQC_BT, qm->cqc_dma, 0, 0);
-       if (ret)
-               return ret;
+       if (qp->qm->wq)
+               flush_workqueue(qp->qm->wq);
+       else
+               flush_work(&qp->qm->work);
 
-       writel(0x0, qm->io_base + QM_VF_EQ_INT_MASK);
-       writel(0x0, qm->io_base + QM_VF_AEQ_INT_MASK);
+       dev_dbg(dev, "stop queue %u!", qp->qp_id);
 
        return 0;
 }
 
 /**
- * hisi_qm_start() - start qm
- * @qm: The qm to be started.
+ * hisi_qm_stop_qp() - Stop a qp in qm.
+ * @qp: The qp we want to stop.
  *
- * This function starts a qm, then we can allocate qp from this qm.
+ * This function is reverse of hisi_qm_start_qp. Return 0 if successful.
  */
-int hisi_qm_start(struct hisi_qm *qm)
+int hisi_qm_stop_qp(struct hisi_qp *qp)
 {
-       struct device *dev = &qm->pdev->dev;
+       int ret;
 
-       dev_dbg(dev, "qm start with %d queue pairs\n", qm->qp_num);
+       down_write(&qp->qm->qps_lock);
+       ret = qm_stop_qp_nolock(qp);
+       up_write(&qp->qm->qps_lock);
 
-       if (!qm->qp_num) {
-               dev_err(dev, "qp_num should not be 0\n");
-               return -EINVAL;
-       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_stop_qp);
 
-       if (!qm->qp_bitmap) {
-               qm->qp_bitmap = devm_kcalloc(dev, BITS_TO_LONGS(qm->qp_num),
-                                            sizeof(long), GFP_KERNEL);
-               qm->qp_array = devm_kcalloc(dev, qm->qp_num,
-                                           sizeof(struct hisi_qp *),
-                                           GFP_KERNEL);
-               if (!qm->qp_bitmap || !qm->qp_array)
-                       return -ENOMEM;
-       }
+/**
+ * hisi_qp_send() - Queue up a task in the hardware queue.
+ * @qp: The qp in which to put the message.
+ * @msg: The message.
+ *
+ * This function will return -EBUSY if qp is currently full, and -EAGAIN
+ * if qp related qm is resetting.
+ *
+ * Note: This function may run with qm_irq_thread and ACC reset at same time.
+ *       It has no race with qm_irq_thread. However, during hisi_qp_send, ACC
+ *       reset may happen, we have no lock here considering performance. This
+ *       causes current qm_db sending fail or can not receive sended sqe. QM
+ *       sync/async receive function should handle the error sqe. ACC reset
+ *       done function should clear used sqe to 0.
+ */
+int hisi_qp_send(struct hisi_qp *qp, const void *msg)
+{
+       struct hisi_qp_status *qp_status = &qp->qp_status;
+       u16 sq_tail = qp_status->sq_tail;
+       u16 sq_tail_next = (sq_tail + 1) % QM_Q_DEPTH;
+       void *sqe = qm_get_avail_sqe(qp);
+
+       if (unlikely(atomic_read(&qp->qp_status.flags) == QP_STOP ||
+                    atomic_read(&qp->qm->status.flags) == QM_STOP ||
+                    qp->is_resetting)) {
+               dev_info(&qp->qm->pdev->dev, "QP is stopped or resetting\n");
+               return -EAGAIN;
+       }
+
+       if (!sqe)
+               return -EBUSY;
+
+       memcpy(sqe, msg, qp->qm->sqe_size);
+
+       qm_db(qp->qm, qp->qp_id, QM_DOORBELL_CMD_SQ, sq_tail_next, 0);
+       atomic_inc(&qp->qp_status.used);
+       qp_status->sq_tail = sq_tail_next;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_qp_send);
+
+static void hisi_qm_cache_wb(struct hisi_qm *qm)
+{
+       unsigned int val;
+
+       if (qm->ver == QM_HW_V1)
+               return;
+
+       writel(0x1, qm->io_base + QM_CACHE_WB_START);
+       if (readl_relaxed_poll_timeout(qm->io_base + QM_CACHE_WB_DONE,
+                                           val, val & BIT(0), 10, 1000))
+               dev_err(&qm->pdev->dev, "QM writeback sqc cache fail!\n");
+}
+
+static void qm_qp_event_notifier(struct hisi_qp *qp)
+{
+       wake_up_interruptible(&qp->uacce_q->wait);
+}
+
+static int hisi_qm_get_available_instances(struct uacce_device *uacce)
+{
+       return hisi_qm_get_free_qp_num(uacce->priv);
+}
+
+static int hisi_qm_uacce_get_queue(struct uacce_device *uacce,
+                                  unsigned long arg,
+                                  struct uacce_queue *q)
+{
+       struct hisi_qm *qm = uacce->priv;
+       struct hisi_qp *qp;
+       u8 alg_type = 0;
+
+       qp = hisi_qm_create_qp(qm, alg_type);
+       if (IS_ERR(qp))
+               return PTR_ERR(qp);
+
+       q->priv = qp;
+       q->uacce = uacce;
+       qp->uacce_q = q;
+       qp->event_cb = qm_qp_event_notifier;
+       qp->pasid = arg;
+
+       return 0;
+}
+
+static void hisi_qm_uacce_put_queue(struct uacce_queue *q)
+{
+       struct hisi_qp *qp = q->priv;
+
+       hisi_qm_cache_wb(qp->qm);
+       hisi_qm_release_qp(qp);
+}
+
+/* map sq/cq/doorbell to user space */
+static int hisi_qm_uacce_mmap(struct uacce_queue *q,
+                             struct vm_area_struct *vma,
+                             struct uacce_qfile_region *qfr)
+{
+       struct hisi_qp *qp = q->priv;
+       struct hisi_qm *qm = qp->qm;
+       size_t sz = vma->vm_end - vma->vm_start;
+       struct pci_dev *pdev = qm->pdev;
+       struct device *dev = &pdev->dev;
+       unsigned long vm_pgoff;
+       int ret;
+
+       switch (qfr->type) {
+       case UACCE_QFRT_MMIO:
+               if (qm->ver == QM_HW_V1) {
+                       if (sz > PAGE_SIZE * QM_DOORBELL_PAGE_NR)
+                               return -EINVAL;
+               } else {
+                       if (sz > PAGE_SIZE * (QM_DOORBELL_PAGE_NR +
+                           QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE))
+                               return -EINVAL;
+               }
+
+               vma->vm_flags |= VM_IO;
+
+               return remap_pfn_range(vma, vma->vm_start,
+                                      qm->phys_base >> PAGE_SHIFT,
+                                      sz, pgprot_noncached(vma->vm_page_prot));
+       case UACCE_QFRT_DUS:
+               if (sz != qp->qdma.size)
+                       return -EINVAL;
+
+               /*
+                * dma_mmap_coherent() requires vm_pgoff as 0
+                * restore vm_pfoff to initial value for mmap()
+                */
+               vm_pgoff = vma->vm_pgoff;
+               vma->vm_pgoff = 0;
+               ret = dma_mmap_coherent(dev, vma, qp->qdma.va,
+                                       qp->qdma.dma, sz);
+               vma->vm_pgoff = vm_pgoff;
+               return ret;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int hisi_qm_uacce_start_queue(struct uacce_queue *q)
+{
+       struct hisi_qp *qp = q->priv;
+
+       return hisi_qm_start_qp(qp, qp->pasid);
+}
+
+static void hisi_qm_uacce_stop_queue(struct uacce_queue *q)
+{
+       hisi_qm_stop_qp(q->priv);
+}
+
+static int qm_set_sqctype(struct uacce_queue *q, u16 type)
+{
+       struct hisi_qm *qm = q->uacce->priv;
+       struct hisi_qp *qp = q->priv;
+
+       down_write(&qm->qps_lock);
+       qp->alg_type = type;
+       up_write(&qm->qps_lock);
+
+       return 0;
+}
+
+static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd,
+                               unsigned long arg)
+{
+       struct hisi_qp *qp = q->priv;
+       struct hisi_qp_ctx qp_ctx;
+
+       if (cmd == UACCE_CMD_QM_SET_QP_CTX) {
+               if (copy_from_user(&qp_ctx, (void __user *)arg,
+                                  sizeof(struct hisi_qp_ctx)))
+                       return -EFAULT;
+
+               if (qp_ctx.qc_type != 0 && qp_ctx.qc_type != 1)
+                       return -EINVAL;
+
+               qm_set_sqctype(q, qp_ctx.qc_type);
+               qp_ctx.id = qp->qp_id;
+
+               if (copy_to_user((void __user *)arg, &qp_ctx,
+                                sizeof(struct hisi_qp_ctx)))
+                       return -EFAULT;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct uacce_ops uacce_qm_ops = {
+       .get_available_instances = hisi_qm_get_available_instances,
+       .get_queue = hisi_qm_uacce_get_queue,
+       .put_queue = hisi_qm_uacce_put_queue,
+       .start_queue = hisi_qm_uacce_start_queue,
+       .stop_queue = hisi_qm_uacce_stop_queue,
+       .mmap = hisi_qm_uacce_mmap,
+       .ioctl = hisi_qm_uacce_ioctl,
+};
+
+static int qm_alloc_uacce(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       struct uacce_device *uacce;
+       unsigned long mmio_page_nr;
+       unsigned long dus_page_nr;
+       struct uacce_interface interface = {
+               .flags = UACCE_DEV_SVA,
+               .ops = &uacce_qm_ops,
+       };
+
+       strncpy(interface.name, pdev->driver->name, sizeof(interface.name));
+
+       uacce = uacce_alloc(&pdev->dev, &interface);
+       if (IS_ERR(uacce))
+               return PTR_ERR(uacce);
+
+       if (uacce->flags & UACCE_DEV_SVA) {
+               qm->use_sva = true;
+       } else {
+               /* only consider sva case */
+               uacce_remove(uacce);
+               qm->uacce = NULL;
+               return -EINVAL;
+       }
+
+       uacce->is_vf = pdev->is_virtfn;
+       uacce->priv = qm;
+       uacce->algs = qm->algs;
+
+       if (qm->ver == QM_HW_V1) {
+               mmio_page_nr = QM_DOORBELL_PAGE_NR;
+               uacce->api_ver = HISI_QM_API_VER_BASE;
+       } else {
+               mmio_page_nr = QM_DOORBELL_PAGE_NR +
+                       QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE;
+               uacce->api_ver = HISI_QM_API_VER2_BASE;
+       }
+
+       dus_page_nr = (PAGE_SIZE - 1 + qm->sqe_size * QM_Q_DEPTH +
+                      sizeof(struct qm_cqe) * QM_Q_DEPTH) >> PAGE_SHIFT;
+
+       uacce->qf_pg_num[UACCE_QFRT_MMIO] = mmio_page_nr;
+       uacce->qf_pg_num[UACCE_QFRT_DUS]  = dus_page_nr;
+
+       qm->uacce = uacce;
+
+       return 0;
+}
+
+/**
+ * hisi_qm_get_free_qp_num() - Get free number of qp in qm.
+ * @qm: The qm which want to get free qp.
+ *
+ * This function return free number of qp in qm.
+ */
+int hisi_qm_get_free_qp_num(struct hisi_qm *qm)
+{
+       int ret;
+
+       down_read(&qm->qps_lock);
+       ret = qm->qp_num - qm->qp_in_used;
+       up_read(&qm->qps_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_get_free_qp_num);
+
+static void hisi_qp_memory_uninit(struct hisi_qm *qm, int num)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct qm_dma *qdma;
+       int i;
+
+       for (i = num - 1; i >= 0; i--) {
+               qdma = &qm->qp_array[i].qdma;
+               dma_free_coherent(dev, qdma->size, qdma->va, qdma->dma);
+       }
+
+       kfree(qm->qp_array);
+}
+
+static int hisi_qp_memory_init(struct hisi_qm *qm, size_t dma_size, int id)
+{
+       struct device *dev = &qm->pdev->dev;
+       size_t off = qm->sqe_size * QM_Q_DEPTH;
+       struct hisi_qp *qp;
+
+       qp = &qm->qp_array[id];
+       qp->qdma.va = dma_alloc_coherent(dev, dma_size, &qp->qdma.dma,
+                                        GFP_KERNEL);
+       if (!qp->qdma.va)
+               return -ENOMEM;
+
+       qp->sqe = qp->qdma.va;
+       qp->sqe_dma = qp->qdma.dma;
+       qp->cqe = qp->qdma.va + off;
+       qp->cqe_dma = qp->qdma.dma + off;
+       qp->qdma.size = dma_size;
+       qp->qm = qm;
+       qp->qp_id = id;
+
+       return 0;
+}
+
+static int hisi_qm_memory_init(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       size_t qp_dma_size, off = 0;
+       int i, ret = 0;
+
+#define QM_INIT_BUF(qm, type, num) do { \
+       (qm)->type = ((qm)->qdma.va + (off)); \
+       (qm)->type##_dma = (qm)->qdma.dma + (off); \
+       off += QMC_ALIGN(sizeof(struct qm_##type) * (num)); \
+} while (0)
+
+       idr_init(&qm->qp_idr);
+       qm->qdma.size = QMC_ALIGN(sizeof(struct qm_eqe) * QM_Q_DEPTH) +
+                       QMC_ALIGN(sizeof(struct qm_aeqe) * QM_Q_DEPTH) +
+                       QMC_ALIGN(sizeof(struct qm_sqc) * qm->qp_num) +
+                       QMC_ALIGN(sizeof(struct qm_cqc) * qm->qp_num);
+       qm->qdma.va = dma_alloc_coherent(dev, qm->qdma.size, &qm->qdma.dma,
+                                        GFP_ATOMIC);
+       dev_dbg(dev, "allocate qm dma buf size=%zx)\n", qm->qdma.size);
+       if (!qm->qdma.va)
+               return -ENOMEM;
+
+       QM_INIT_BUF(qm, eqe, QM_Q_DEPTH);
+       QM_INIT_BUF(qm, aeqe, QM_Q_DEPTH);
+       QM_INIT_BUF(qm, sqc, qm->qp_num);
+       QM_INIT_BUF(qm, cqc, qm->qp_num);
+
+       qm->qp_array = kcalloc(qm->qp_num, sizeof(struct hisi_qp), GFP_KERNEL);
+       if (!qm->qp_array) {
+               ret = -ENOMEM;
+               goto err_alloc_qp_array;
+       }
+
+       /* one more page for device or qp statuses */
+       qp_dma_size = qm->sqe_size * QM_Q_DEPTH +
+                     sizeof(struct qm_cqe) * QM_Q_DEPTH;
+       qp_dma_size = PAGE_ALIGN(qp_dma_size);
+       for (i = 0; i < qm->qp_num; i++) {
+               ret = hisi_qp_memory_init(qm, qp_dma_size, i);
+               if (ret)
+                       goto err_init_qp_mem;
+
+               dev_dbg(dev, "allocate qp dma buf size=%zx)\n", qp_dma_size);
+       }
+
+       return ret;
+
+err_init_qp_mem:
+       hisi_qp_memory_uninit(qm, i);
+err_alloc_qp_array:
+       dma_free_coherent(dev, qm->qdma.size, qm->qdma.va, qm->qdma.dma);
+
+       return ret;
+}
+
+static void hisi_qm_pre_init(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+
+       if (qm->ver == QM_HW_V1)
+               qm->ops = &qm_hw_ops_v1;
+       else
+               qm->ops = &qm_hw_ops_v2;
+
+       pci_set_drvdata(pdev, qm);
+       mutex_init(&qm->mailbox_lock);
+       init_rwsem(&qm->qps_lock);
+       qm->qp_in_used = 0;
+}
+
+/**
+ * hisi_qm_uninit() - Uninitialize qm.
+ * @qm: The qm needed uninit.
+ *
+ * This function uninits qm related device resources.
+ */
+void hisi_qm_uninit(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       struct device *dev = &pdev->dev;
+
+       down_write(&qm->qps_lock);
+
+       if (!qm_avail_state(qm, QM_CLOSE)) {
+               up_write(&qm->qps_lock);
+               return;
+       }
+
+       uacce_remove(qm->uacce);
+       qm->uacce = NULL;
+
+       hisi_qp_memory_uninit(qm, qm->qp_num);
+       idr_destroy(&qm->qp_idr);
+
+       if (qm->qdma.va) {
+               hisi_qm_cache_wb(qm);
+               dma_free_coherent(dev, qm->qdma.size,
+                                 qm->qdma.va, qm->qdma.dma);
+               memset(&qm->qdma, 0, sizeof(qm->qdma));
+       }
+
+       qm_irq_unregister(qm);
+       pci_free_irq_vectors(pdev);
+       iounmap(qm->io_base);
+       pci_release_mem_regions(pdev);
+       pci_disable_device(pdev);
+
+       up_write(&qm->qps_lock);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_uninit);
+
+/**
+ * hisi_qm_get_vft() - Get vft from a qm.
+ * @qm: The qm we want to get its vft.
+ * @base: The base number of queue in vft.
+ * @number: The number of queues in vft.
+ *
+ * We can allocate multiple queues to a qm by configuring virtual function
+ * table. We get related configures by this function. Normally, we call this
+ * function in VF driver to get the queue information.
+ *
+ * qm hw v1 does not support this interface.
+ */
+int hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number)
+{
+       if (!base || !number)
+               return -EINVAL;
+
+       if (!qm->ops->get_vft) {
+               dev_err(&qm->pdev->dev, "Don't support vft read!\n");
+               return -EINVAL;
+       }
+
+       return qm->ops->get_vft(qm, base, number);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_get_vft);
+
+/**
+ * This function is alway called in PF driver, it is used to assign queues
+ * among PF and VFs.
+ *
+ * Assign queues A~B to PF: hisi_qm_set_vft(qm, 0, A, B - A + 1)
+ * Assign queues A~B to VF: hisi_qm_set_vft(qm, 2, A, B - A + 1)
+ * (VF function number 0x2)
+ */
+static int hisi_qm_set_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
+                   u32 number)
+{
+       u32 max_q_num = qm->ctrl_qp_num;
+
+       if (base >= max_q_num || number > max_q_num ||
+           (base + number) > max_q_num)
+               return -EINVAL;
+
+       return qm_set_sqc_cqc_vft(qm, fun_num, base, number);
+}
+
+static void qm_init_eq_aeq_status(struct hisi_qm *qm)
+{
+       struct hisi_qm_status *status = &qm->status;
+
+       status->eq_head = 0;
+       status->aeq_head = 0;
+       status->eqc_phase = true;
+       status->aeqc_phase = true;
+}
+
+static int qm_eq_ctx_cfg(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct qm_eqc *eqc;
+       struct qm_aeqc *aeqc;
+       dma_addr_t eqc_dma;
+       dma_addr_t aeqc_dma;
+       int ret;
+
+       qm_init_eq_aeq_status(qm);
+
+       eqc = kzalloc(sizeof(struct qm_eqc), GFP_KERNEL);
+       if (!eqc)
+               return -ENOMEM;
+       eqc_dma = dma_map_single(dev, eqc, sizeof(struct qm_eqc),
+                                DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, eqc_dma)) {
+               kfree(eqc);
+               return -ENOMEM;
+       }
+
+       eqc->base_l = cpu_to_le32(lower_32_bits(qm->eqe_dma));
+       eqc->base_h = cpu_to_le32(upper_32_bits(qm->eqe_dma));
+       if (qm->ver == QM_HW_V1)
+               eqc->dw3 = cpu_to_le32(QM_EQE_AEQE_SIZE);
+       eqc->dw6 = cpu_to_le32((QM_Q_DEPTH - 1) | (1 << QM_EQC_PHASE_SHIFT));
+       ret = qm_mb(qm, QM_MB_CMD_EQC, eqc_dma, 0, 0);
+       dma_unmap_single(dev, eqc_dma, sizeof(struct qm_eqc), DMA_TO_DEVICE);
+       kfree(eqc);
+       if (ret)
+               return ret;
+
+       aeqc = kzalloc(sizeof(struct qm_aeqc), GFP_KERNEL);
+       if (!aeqc)
+               return -ENOMEM;
+       aeqc_dma = dma_map_single(dev, aeqc, sizeof(struct qm_aeqc),
+                                 DMA_TO_DEVICE);
+       if (dma_mapping_error(dev, aeqc_dma)) {
+               kfree(aeqc);
+               return -ENOMEM;
+       }
+
+       aeqc->base_l = cpu_to_le32(lower_32_bits(qm->aeqe_dma));
+       aeqc->base_h = cpu_to_le32(upper_32_bits(qm->aeqe_dma));
+       aeqc->dw6 = cpu_to_le32((QM_Q_DEPTH - 1) | (1 << QM_EQC_PHASE_SHIFT));
+
+       ret = qm_mb(qm, QM_MB_CMD_AEQC, aeqc_dma, 0, 0);
+       dma_unmap_single(dev, aeqc_dma, sizeof(struct qm_aeqc), DMA_TO_DEVICE);
+       kfree(aeqc);
+
+       return ret;
+}
+
+static int __hisi_qm_start(struct hisi_qm *qm)
+{
+       int ret;
+
+       WARN_ON(!qm->qdma.dma);
+
+       if (qm->fun_type == QM_HW_PF) {
+               ret = qm_dev_mem_reset(qm);
+               if (ret)
+                       return ret;
+
+               ret = hisi_qm_set_vft(qm, 0, qm->qp_base, qm->qp_num);
+               if (ret)
+                       return ret;
+       }
+
+       ret = qm_eq_ctx_cfg(qm);
+       if (ret)
+               return ret;
+
+       ret = qm_mb(qm, QM_MB_CMD_SQC_BT, qm->sqc_dma, 0, 0);
+       if (ret)
+               return ret;
+
+       ret = qm_mb(qm, QM_MB_CMD_CQC_BT, qm->cqc_dma, 0, 0);
+       if (ret)
+               return ret;
+
+       writel(0x0, qm->io_base + QM_VF_EQ_INT_MASK);
+       writel(0x0, qm->io_base + QM_VF_AEQ_INT_MASK);
+
+       return 0;
+}
+
+/**
+ * hisi_qm_start() - start qm
+ * @qm: The qm to be started.
+ *
+ * This function starts a qm, then we can allocate qp from this qm.
+ */
+int hisi_qm_start(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       int ret = 0;
+
+       down_write(&qm->qps_lock);
+
+       if (!qm_avail_state(qm, QM_START)) {
+               up_write(&qm->qps_lock);
+               return -EPERM;
+       }
+
+       dev_dbg(dev, "qm start with %d queue pairs\n", qm->qp_num);
+
+       if (!qm->qp_num) {
+               dev_err(dev, "qp_num should not be 0\n");
+               ret = -EINVAL;
+               goto err_unlock;
+       }
+
+       ret = __hisi_qm_start(qm);
+       if (!ret)
+               atomic_set(&qm->status.flags, QM_START);
+
+err_unlock:
+       up_write(&qm->qps_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_start);
+
+static int qm_restart(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct hisi_qp *qp;
+       int ret, i;
+
+       ret = hisi_qm_start(qm);
+       if (ret < 0)
+               return ret;
+
+       down_write(&qm->qps_lock);
+       for (i = 0; i < qm->qp_num; i++) {
+               qp = &qm->qp_array[i];
+               if (atomic_read(&qp->qp_status.flags) == QP_STOP &&
+                   qp->is_resetting == true) {
+                       ret = qm_start_qp_nolock(qp, 0);
+                       if (ret < 0) {
+                               dev_err(dev, "Failed to start qp%d!\n", i);
+
+                               up_write(&qm->qps_lock);
+                               return ret;
+                       }
+                       qp->is_resetting = false;
+               }
+       }
+       up_write(&qm->qps_lock);
+
+       return 0;
+}
+
+/* Stop started qps in reset flow */
+static int qm_stop_started_qp(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       struct hisi_qp *qp;
+       int i, ret;
+
+       for (i = 0; i < qm->qp_num; i++) {
+               qp = &qm->qp_array[i];
+               if (qp && atomic_read(&qp->qp_status.flags) == QP_START) {
+                       qp->is_resetting = true;
+                       ret = qm_stop_qp_nolock(qp);
+                       if (ret < 0) {
+                               dev_err(dev, "Failed to stop qp%d!\n", i);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * This function clears all queues memory in a qm. Reset of accelerator can
+ * use this to clear queues.
+ */
+static void qm_clear_queues(struct hisi_qm *qm)
+{
+       struct hisi_qp *qp;
+       int i;
+
+       for (i = 0; i < qm->qp_num; i++) {
+               qp = &qm->qp_array[i];
+               if (qp->is_resetting)
+                       memset(qp->qdma.va, 0, qp->qdma.size);
+       }
+
+       memset(qm->qdma.va, 0, qm->qdma.size);
+}
+
+/**
+ * hisi_qm_stop() - Stop a qm.
+ * @qm: The qm which will be stopped.
+ *
+ * This function stops qm and its qps, then qm can not accept request.
+ * Related resources are not released at this state, we can use hisi_qm_start
+ * to let qm start again.
+ */
+int hisi_qm_stop(struct hisi_qm *qm)
+{
+       struct device *dev = &qm->pdev->dev;
+       int ret = 0;
+
+       down_write(&qm->qps_lock);
+
+       if (!qm_avail_state(qm, QM_STOP)) {
+               ret = -EPERM;
+               goto err_unlock;
+       }
+
+       if (qm->status.stop_reason == QM_SOFT_RESET ||
+           qm->status.stop_reason == QM_FLR) {
+               ret = qm_stop_started_qp(qm);
+               if (ret < 0) {
+                       dev_err(dev, "Failed to stop started qp!\n");
+                       goto err_unlock;
+               }
+       }
+
+       /* Mask eq and aeq irq */
+       writel(0x1, qm->io_base + QM_VF_EQ_INT_MASK);
+       writel(0x1, qm->io_base + QM_VF_AEQ_INT_MASK);
+
+       if (qm->fun_type == QM_HW_PF) {
+               ret = hisi_qm_set_vft(qm, 0, 0, 0);
+               if (ret < 0) {
+                       dev_err(dev, "Failed to set vft!\n");
+                       ret = -EBUSY;
+                       goto err_unlock;
+               }
+       }
+
+       qm_clear_queues(qm);
+       atomic_set(&qm->status.flags, QM_STOP);
+
+err_unlock:
+       up_write(&qm->qps_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_stop);
+
+static ssize_t qm_status_read(struct file *filp, char __user *buffer,
+                             size_t count, loff_t *pos)
+{
+       struct hisi_qm *qm = filp->private_data;
+       char buf[QM_DBG_READ_LEN];
+       int val, cp_len, len;
+
+       if (*pos)
+               return 0;
+
+       if (count < QM_DBG_READ_LEN)
+               return -ENOSPC;
+
+       val = atomic_read(&qm->status.flags);
+       len = snprintf(buf, QM_DBG_READ_LEN, "%s\n", qm_s[val]);
+       if (!len)
+               return -EFAULT;
+
+       cp_len = copy_to_user(buffer, buf, len);
+       if (cp_len)
+               return -EFAULT;
+
+       return (*pos = len);
+}
+
+static const struct file_operations qm_status_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = qm_status_read,
+};
+
+static int qm_debugfs_atomic64_set(void *data, u64 val)
+{
+       if (val)
+               return -EINVAL;
+
+       atomic64_set((atomic64_t *)data, 0);
+
+       return 0;
+}
+
+static int qm_debugfs_atomic64_get(void *data, u64 *val)
+{
+       *val = atomic64_read((atomic64_t *)data);
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(qm_atomic64_ops, qm_debugfs_atomic64_get,
+                        qm_debugfs_atomic64_set, "%llu\n");
+
+/**
+ * hisi_qm_debug_init() - Initialize qm related debugfs files.
+ * @qm: The qm for which we want to add debugfs files.
+ *
+ * Create qm related debugfs files.
+ */
+int hisi_qm_debug_init(struct hisi_qm *qm)
+{
+       struct qm_dfx *dfx = &qm->debug.dfx;
+       struct dentry *qm_d;
+       void *data;
+       int i, ret;
+
+       qm_d = debugfs_create_dir("qm", qm->debug.debug_root);
+       qm->debug.qm_d = qm_d;
+
+       /* only show this in PF */
+       if (qm->fun_type == QM_HW_PF)
+               for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++)
+                       if (qm_create_debugfs_file(qm, i)) {
+                               ret = -ENOENT;
+                               goto failed_to_create;
+                       }
+
+       debugfs_create_file("regs", 0444, qm->debug.qm_d, qm, &qm_regs_fops);
+
+       debugfs_create_file("cmd", 0444, qm->debug.qm_d, qm, &qm_cmd_fops);
+
+       debugfs_create_file("status", 0444, qm->debug.qm_d, qm,
+                       &qm_status_fops);
+       for (i = 0; i < ARRAY_SIZE(qm_dfx_files); i++) {
+               data = (atomic64_t *)((uintptr_t)dfx + qm_dfx_files[i].offset);
+               debugfs_create_file(qm_dfx_files[i].name,
+                       0644,
+                       qm_d,
+                       data,
+                       &qm_atomic64_ops);
+       }
+
+       return 0;
+
+failed_to_create:
+       debugfs_remove_recursive(qm_d);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_debug_init);
+
+/**
+ * hisi_qm_debug_regs_clear() - clear qm debug related registers.
+ * @qm: The qm for which we want to clear its debug registers.
+ */
+void hisi_qm_debug_regs_clear(struct hisi_qm *qm)
+{
+       struct qm_dfx_registers *regs;
+       int i;
+
+       /* clear current_q */
+       writel(0x0, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
+       writel(0x0, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
+
+       /*
+        * these registers are reading and clearing, so clear them after
+        * reading them.
+        */
+       writel(0x1, qm->io_base + QM_DFX_CNT_CLR_CE);
+
+       regs = qm_dfx_regs;
+       for (i = 0; i < CNT_CYC_REGS_NUM; i++) {
+               readl(qm->io_base + regs->reg_offset);
+               regs++;
+       }
+
+       writel(0x0, qm->io_base + QM_DFX_CNT_CLR_CE);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear);
+
+static void qm_hw_error_init(struct hisi_qm *qm)
+{
+       const struct hisi_qm_err_info *err_info = &qm->err_ini->err_info;
+
+       if (!qm->ops->hw_error_init) {
+               dev_err(&qm->pdev->dev, "QM doesn't support hw error handling!\n");
+               return;
+       }
+
+       qm->ops->hw_error_init(qm, err_info->ce, err_info->nfe, err_info->fe);
+}
+
+static void qm_hw_error_uninit(struct hisi_qm *qm)
+{
+       if (!qm->ops->hw_error_uninit) {
+               dev_err(&qm->pdev->dev, "Unexpected QM hw error uninit!\n");
+               return;
+       }
+
+       qm->ops->hw_error_uninit(qm);
+}
+
+static enum acc_err_result qm_hw_error_handle(struct hisi_qm *qm)
+{
+       if (!qm->ops->hw_error_handle) {
+               dev_err(&qm->pdev->dev, "QM doesn't support hw error report!\n");
+               return ACC_ERR_NONE;
+       }
+
+       return qm->ops->hw_error_handle(qm);
+}
+
+/**
+ * hisi_qm_dev_err_init() - Initialize device error configuration.
+ * @qm: The qm for which we want to do error initialization.
+ *
+ * Initialize QM and device error related configuration.
+ */
+void hisi_qm_dev_err_init(struct hisi_qm *qm)
+{
+       if (qm->fun_type == QM_HW_VF)
+               return;
+
+       qm_hw_error_init(qm);
+
+       if (!qm->err_ini->hw_err_enable) {
+               dev_err(&qm->pdev->dev, "Device doesn't support hw error init!\n");
+               return;
+       }
+       qm->err_ini->hw_err_enable(qm);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_dev_err_init);
+
+/**
+ * hisi_qm_dev_err_uninit() - Uninitialize device error configuration.
+ * @qm: The qm for which we want to do error uninitialization.
+ *
+ * Uninitialize QM and device error related configuration.
+ */
+void hisi_qm_dev_err_uninit(struct hisi_qm *qm)
+{
+       if (qm->fun_type == QM_HW_VF)
+               return;
+
+       qm_hw_error_uninit(qm);
+
+       if (!qm->err_ini->hw_err_disable) {
+               dev_err(&qm->pdev->dev, "Unexpected device hw error uninit!\n");
+               return;
+       }
+       qm->err_ini->hw_err_disable(qm);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_dev_err_uninit);
+
+/**
+ * hisi_qm_free_qps() - free multiple queue pairs.
+ * @qps: The queue pairs need to be freed.
+ * @qp_num: The num of queue pairs.
+ */
+void hisi_qm_free_qps(struct hisi_qp **qps, int qp_num)
+{
+       int i;
+
+       if (!qps || qp_num <= 0)
+               return;
+
+       for (i = qp_num - 1; i >= 0; i--)
+               hisi_qm_release_qp(qps[i]);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_free_qps);
+
+static void free_list(struct list_head *head)
+{
+       struct hisi_qm_resource *res, *tmp;
+
+       list_for_each_entry_safe(res, tmp, head, list) {
+               list_del(&res->list);
+               kfree(res);
+       }
+}
+
+static int hisi_qm_sort_devices(int node, struct list_head *head,
+                               struct hisi_qm_list *qm_list)
+{
+       struct hisi_qm_resource *res, *tmp;
+       struct hisi_qm *qm;
+       struct list_head *n;
+       struct device *dev;
+       int dev_node = 0;
+
+       list_for_each_entry(qm, &qm_list->list, list) {
+               dev = &qm->pdev->dev;
+
+               if (IS_ENABLED(CONFIG_NUMA)) {
+                       dev_node = dev_to_node(dev);
+                       if (dev_node < 0)
+                               dev_node = 0;
+               }
+
+               res = kzalloc(sizeof(*res), GFP_KERNEL);
+               if (!res)
+                       return -ENOMEM;
+
+               res->qm = qm;
+               res->distance = node_distance(dev_node, node);
+               n = head;
+               list_for_each_entry(tmp, head, list) {
+                       if (res->distance < tmp->distance) {
+                               n = &tmp->list;
+                               break;
+                       }
+               }
+               list_add_tail(&res->list, n);
+       }
+
+       return 0;
+}
+
+/**
+ * hisi_qm_alloc_qps_node() - Create multiple queue pairs.
+ * @qm_list: The list of all available devices.
+ * @qp_num: The number of queue pairs need created.
+ * @alg_type: The algorithm type.
+ * @node: The numa node.
+ * @qps: The queue pairs need created.
+ *
+ * This function will sort all available device according to numa distance.
+ * Then try to create all queue pairs from one device, if all devices do
+ * not meet the requirements will return error.
+ */
+int hisi_qm_alloc_qps_node(struct hisi_qm_list *qm_list, int qp_num,
+                          u8 alg_type, int node, struct hisi_qp **qps)
+{
+       struct hisi_qm_resource *tmp;
+       int ret = -ENODEV;
+       LIST_HEAD(head);
+       int i;
+
+       if (!qps || !qm_list || qp_num <= 0)
+               return -EINVAL;
+
+       mutex_lock(&qm_list->lock);
+       if (hisi_qm_sort_devices(node, &head, qm_list)) {
+               mutex_unlock(&qm_list->lock);
+               goto err;
+       }
+
+       list_for_each_entry(tmp, &head, list) {
+               for (i = 0; i < qp_num; i++) {
+                       qps[i] = hisi_qm_create_qp(tmp->qm, alg_type);
+                       if (IS_ERR(qps[i])) {
+                               hisi_qm_free_qps(qps, i);
+                               break;
+                       }
+               }
+
+               if (i == qp_num) {
+                       ret = 0;
+                       break;
+               }
+       }
+
+       mutex_unlock(&qm_list->lock);
+       if (ret)
+               pr_info("Failed to create qps, node[%d], alg[%d], qp[%d]!\n",
+                       node, alg_type, qp_num);
+
+err:
+       free_list(&head);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_alloc_qps_node);
+
+static int qm_vf_q_assign(struct hisi_qm *qm, u32 num_vfs)
+{
+       u32 remain_q_num, q_num, i, j;
+       u32 q_base = qm->qp_num;
+       int ret;
+
+       if (!num_vfs)
+               return -EINVAL;
+
+       remain_q_num = qm->ctrl_qp_num - qm->qp_num;
+
+       /* If remain queues not enough, return error. */
+       if (qm->ctrl_qp_num < qm->qp_num || remain_q_num < num_vfs)
+               return -EINVAL;
+
+       q_num = remain_q_num / num_vfs;
+       for (i = 1; i <= num_vfs; i++) {
+               if (i == num_vfs)
+                       q_num += remain_q_num % num_vfs;
+               ret = hisi_qm_set_vft(qm, i, q_base, q_num);
+               if (ret) {
+                       for (j = i; j > 0; j--)
+                               hisi_qm_set_vft(qm, j, 0, 0);
+                       return ret;
+               }
+               q_base += q_num;
+       }
+
+       return 0;
+}
+
+static int qm_clear_vft_config(struct hisi_qm *qm)
+{
+       int ret;
+       u32 i;
+
+       for (i = 1; i <= qm->vfs_num; i++) {
+               ret = hisi_qm_set_vft(qm, i, 0, 0);
+               if (ret)
+                       return ret;
+       }
+       qm->vfs_num = 0;
+
+       return 0;
+}
+
+/**
+ * hisi_qm_sriov_enable() - enable virtual functions
+ * @pdev: the PCIe device
+ * @max_vfs: the number of virtual functions to enable
+ *
+ * Returns the number of enabled VFs. If there are VFs enabled already or
+ * max_vfs is more than the total number of device can be enabled, returns
+ * failure.
+ */
+int hisi_qm_sriov_enable(struct pci_dev *pdev, int max_vfs)
+{
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+       int pre_existing_vfs, num_vfs, total_vfs, ret;
+
+       total_vfs = pci_sriov_get_totalvfs(pdev);
+       pre_existing_vfs = pci_num_vf(pdev);
+       if (pre_existing_vfs) {
+               pci_err(pdev, "%d VFs already enabled. Please disable pre-enabled VFs!\n",
+                       pre_existing_vfs);
+               return 0;
+       }
+
+       num_vfs = min_t(int, max_vfs, total_vfs);
+       ret = qm_vf_q_assign(qm, num_vfs);
+       if (ret) {
+               pci_err(pdev, "Can't assign queues for VF!\n");
+               return ret;
+       }
+
+       qm->vfs_num = num_vfs;
+
+       ret = pci_enable_sriov(pdev, num_vfs);
+       if (ret) {
+               pci_err(pdev, "Can't enable VF!\n");
+               qm_clear_vft_config(qm);
+               return ret;
+       }
+
+       pci_info(pdev, "VF enabled, vfs_num(=%d)!\n", num_vfs);
+
+       return num_vfs;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_sriov_enable);
+
+/**
+ * hisi_qm_sriov_disable - disable virtual functions
+ * @pdev: the PCI device
+ *
+ * Return failure if there are VFs assigned already.
+ */
+int hisi_qm_sriov_disable(struct pci_dev *pdev)
+{
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+
+       if (pci_vfs_assigned(pdev)) {
+               pci_err(pdev, "Failed to disable VFs as VFs are assigned!\n");
+               return -EPERM;
+       }
+
+       /* remove in hpre_pci_driver will be called to free VF resources */
+       pci_disable_sriov(pdev);
+       return qm_clear_vft_config(qm);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_sriov_disable);
+
+/**
+ * hisi_qm_sriov_configure - configure the number of VFs
+ * @pdev: The PCI device
+ * @num_vfs: The number of VFs need enabled
+ *
+ * Enable SR-IOV according to num_vfs, 0 means disable.
+ */
+int hisi_qm_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+       if (num_vfs == 0)
+               return hisi_qm_sriov_disable(pdev);
+       else
+               return hisi_qm_sriov_enable(pdev, num_vfs);
+}
+EXPORT_SYMBOL_GPL(hisi_qm_sriov_configure);
+
+static enum acc_err_result qm_dev_err_handle(struct hisi_qm *qm)
+{
+       u32 err_sts;
+
+       if (!qm->err_ini->get_dev_hw_err_status) {
+               dev_err(&qm->pdev->dev, "Device doesn't support get hw error status!\n");
+               return ACC_ERR_NONE;
+       }
+
+       /* get device hardware error status */
+       err_sts = qm->err_ini->get_dev_hw_err_status(qm);
+       if (err_sts) {
+               if (err_sts & qm->err_ini->err_info.ecc_2bits_mask)
+                       qm->err_status.is_dev_ecc_mbit = true;
+
+               if (!qm->err_ini->log_dev_hw_err) {
+                       dev_err(&qm->pdev->dev, "Device doesn't support log hw error!\n");
+                       return ACC_ERR_NEED_RESET;
+               }
+
+               qm->err_ini->log_dev_hw_err(qm, err_sts);
+               return ACC_ERR_NEED_RESET;
+       }
+
+       return ACC_ERR_RECOVERED;
+}
+
+static enum acc_err_result qm_process_dev_error(struct hisi_qm *qm)
+{
+       enum acc_err_result qm_ret, dev_ret;
+
+       /* log qm error */
+       qm_ret = qm_hw_error_handle(qm);
+
+       /* log device error */
+       dev_ret = qm_dev_err_handle(qm);
+
+       return (qm_ret == ACC_ERR_NEED_RESET ||
+               dev_ret == ACC_ERR_NEED_RESET) ?
+               ACC_ERR_NEED_RESET : ACC_ERR_RECOVERED;
+}
+
+/**
+ * hisi_qm_dev_err_detected() - Get device and qm error status then log it.
+ * @pdev: The PCI device which need report error.
+ * @state: The connectivity between CPU and device.
+ *
+ * We register this function into PCIe AER handlers, It will report device or
+ * qm hardware error status when error occur.
+ */
+pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev,
+                                         pci_channel_state_t state)
+{
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+       enum acc_err_result ret;
+
+       if (pdev->is_virtfn)
+               return PCI_ERS_RESULT_NONE;
+
+       pci_info(pdev, "PCI error detected, state(=%d)!!\n", state);
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       ret = qm_process_dev_error(qm);
+       if (ret == ACC_ERR_NEED_RESET)
+               return PCI_ERS_RESULT_NEED_RESET;
+
+       return PCI_ERS_RESULT_RECOVERED;
+}
+EXPORT_SYMBOL_GPL(hisi_qm_dev_err_detected);
+
+static int qm_get_hw_error_status(struct hisi_qm *qm)
+{
+       return readl(qm->io_base + QM_ABNORMAL_INT_STATUS);
+}
+
+static int qm_check_req_recv(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
+       u32 val;
+
+       writel(ACC_VENDOR_ID_VALUE, qm->io_base + QM_PEH_VENDOR_ID);
+       ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_VENDOR_ID, val,
+                                        (val == ACC_VENDOR_ID_VALUE),
+                                        POLL_PERIOD, POLL_TIMEOUT);
+       if (ret) {
+               dev_err(&pdev->dev, "Fails to read QM reg!\n");
+               return ret;
+       }
+
+       writel(PCI_VENDOR_ID_HUAWEI, qm->io_base + QM_PEH_VENDOR_ID);
+       ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_VENDOR_ID, val,
+                                        (val == PCI_VENDOR_ID_HUAWEI),
+                                        POLL_PERIOD, POLL_TIMEOUT);
+       if (ret)
+               dev_err(&pdev->dev, "Fails to read QM reg in the second time!\n");
+
+       return ret;
+}
+
+static int qm_set_pf_mse(struct hisi_qm *qm, bool set)
+{
+       struct pci_dev *pdev = qm->pdev;
+       u16 cmd;
+       int i;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       if (set)
+               cmd |= PCI_COMMAND_MEMORY;
+       else
+               cmd &= ~PCI_COMMAND_MEMORY;
+
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+       for (i = 0; i < MAX_WAIT_COUNTS; i++) {
+               pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+               if (set == ((cmd & PCI_COMMAND_MEMORY) >> 1))
+                       return 0;
+
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int qm_set_vf_mse(struct hisi_qm *qm, bool set)
+{
+       struct pci_dev *pdev = qm->pdev;
+       u16 sriov_ctrl;
+       int pos;
+       int i;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+       pci_read_config_word(pdev, pos + PCI_SRIOV_CTRL, &sriov_ctrl);
+       if (set)
+               sriov_ctrl |= PCI_SRIOV_CTRL_MSE;
+       else
+               sriov_ctrl &= ~PCI_SRIOV_CTRL_MSE;
+       pci_write_config_word(pdev, pos + PCI_SRIOV_CTRL, sriov_ctrl);
 
-       if (!qm->use_dma_api) {
-               dev_dbg(&qm->pdev->dev, "qm delay start\n");
-               return 0;
-       } else if (!qm->qdma.va) {
-               qm->qdma.size = QMC_ALIGN(sizeof(struct qm_eqe) * QM_Q_DEPTH) +
-                               QMC_ALIGN(sizeof(struct qm_aeqe) * QM_Q_DEPTH) +
-                               QMC_ALIGN(sizeof(struct qm_sqc) * qm->qp_num) +
-                               QMC_ALIGN(sizeof(struct qm_cqc) * qm->qp_num);
-               qm->qdma.va = dma_alloc_coherent(dev, qm->qdma.size,
-                                                &qm->qdma.dma, GFP_KERNEL);
-               dev_dbg(dev, "allocate qm dma buf(va=%pK, dma=%pad, size=%zx)\n",
-                       qm->qdma.va, &qm->qdma.dma, qm->qdma.size);
-               if (!qm->qdma.va)
-                       return -ENOMEM;
+       for (i = 0; i < MAX_WAIT_COUNTS; i++) {
+               pci_read_config_word(pdev, pos + PCI_SRIOV_CTRL, &sriov_ctrl);
+               if (set == (sriov_ctrl & PCI_SRIOV_CTRL_MSE) >>
+                   ACC_PEH_SRIOV_CTRL_VF_MSE_SHIFT)
+                       return 0;
+
+               udelay(1);
        }
 
-       return __hisi_qm_start(qm);
+       return -ETIMEDOUT;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_start);
 
-/**
- * hisi_qm_stop() - Stop a qm.
- * @qm: The qm which will be stopped.
- *
- * This function stops qm and its qps, then qm can not accept request.
- * Related resources are not released at this state, we can use hisi_qm_start
- * to let qm start again.
- */
-int hisi_qm_stop(struct hisi_qm *qm)
+static int qm_set_msi(struct hisi_qm *qm, bool set)
 {
-       struct device *dev;
-       struct hisi_qp *qp;
-       int ret = 0, i;
+       struct pci_dev *pdev = qm->pdev;
 
-       if (!qm || !qm->pdev) {
-               WARN_ON(1);
-               return -EINVAL;
+       if (set) {
+               pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_MASK_64,
+                                      0);
+       } else {
+               pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_MASK_64,
+                                      ACC_PEH_MSI_DISABLE);
+               if (qm->err_status.is_qm_ecc_mbit ||
+                   qm->err_status.is_dev_ecc_mbit)
+                       return 0;
+
+               mdelay(1);
+               if (readl(qm->io_base + QM_PEH_DFX_INFO0))
+                       return -EFAULT;
        }
 
-       dev = &qm->pdev->dev;
+       return 0;
+}
 
-       /* Mask eq and aeq irq */
-       writel(0x1, qm->io_base + QM_VF_EQ_INT_MASK);
-       writel(0x1, qm->io_base + QM_VF_AEQ_INT_MASK);
+static int qm_vf_reset_prepare(struct hisi_qm *qm)
+{
+       struct hisi_qm_list *qm_list = qm->qm_list;
+       int stop_reason = qm->status.stop_reason;
+       struct pci_dev *pdev = qm->pdev;
+       struct pci_dev *virtfn;
+       struct hisi_qm *vf_qm;
+       int ret = 0;
 
-       /* Stop all qps belong to this qm */
-       for (i = 0; i < qm->qp_num; i++) {
-               qp = qm->qp_array[i];
-               if (qp) {
-                       ret = hisi_qm_stop_qp(qp);
-                       if (ret < 0) {
-                               dev_err(dev, "Failed to stop qp%d!\n", i);
-                               return -EBUSY;
-                       }
+       mutex_lock(&qm_list->lock);
+       list_for_each_entry(vf_qm, &qm_list->list, list) {
+               virtfn = vf_qm->pdev;
+               if (virtfn == pdev)
+                       continue;
+
+               if (pci_physfn(virtfn) == pdev) {
+                       vf_qm->status.stop_reason = stop_reason;
+                       ret = hisi_qm_stop(vf_qm);
+                       if (ret)
+                               goto stop_fail;
                }
        }
 
-       if (qm->fun_type == QM_HW_PF) {
-               ret = hisi_qm_set_vft(qm, 0, 0, 0);
-               if (ret < 0)
-                       dev_err(dev, "Failed to set vft!\n");
+stop_fail:
+       mutex_unlock(&qm_list->lock);
+       return ret;
+}
+
+static int qm_reset_prepare_ready(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));
+       int delay = 0;
+
+       /* All reset requests need to be queued for processing */
+       while (test_and_set_bit(QM_DEV_RESET_FLAG, &pf_qm->reset_flag)) {
+               msleep(++delay);
+               if (delay > QM_RESET_WAIT_TIMEOUT)
+                       return -EBUSY;
        }
 
-       return ret;
+       return 0;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_stop);
 
-/**
- * hisi_qm_debug_init() - Initialize qm related debugfs files.
- * @qm: The qm for which we want to add debugfs files.
- *
- * Create qm related debugfs files.
- */
-int hisi_qm_debug_init(struct hisi_qm *qm)
+static int qm_controller_reset_prepare(struct hisi_qm *qm)
 {
-       struct dentry *qm_d;
-       int i, ret;
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
 
-       qm_d = debugfs_create_dir("qm", qm->debug.debug_root);
-       qm->debug.qm_d = qm_d;
+       ret = qm_reset_prepare_ready(qm);
+       if (ret) {
+               pci_err(pdev, "Controller reset not ready!\n");
+               return ret;
+       }
 
-       /* only show this in PF */
-       if (qm->fun_type == QM_HW_PF)
-               for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++)
-                       if (qm_create_debugfs_file(qm, i)) {
-                               ret = -ENOENT;
-                               goto failed_to_create;
-                       }
+       if (qm->vfs_num) {
+               ret = qm_vf_reset_prepare(qm);
+               if (ret) {
+                       pci_err(pdev, "Fails to stop VFs!\n");
+                       return ret;
+               }
+       }
 
-       debugfs_create_file("qm_regs", 0444, qm->debug.qm_d, qm, &qm_regs_fops);
+       qm->status.stop_reason = QM_SOFT_RESET;
+       ret = hisi_qm_stop(qm);
+       if (ret) {
+               pci_err(pdev, "Fails to stop QM!\n");
+               return ret;
+       }
 
        return 0;
+}
 
-failed_to_create:
-       debugfs_remove_recursive(qm_d);
-       return ret;
+static void qm_dev_ecc_mbit_handle(struct hisi_qm *qm)
+{
+       u32 nfe_enb = 0;
+
+       if (!qm->err_status.is_dev_ecc_mbit &&
+           qm->err_status.is_qm_ecc_mbit &&
+           qm->err_ini->close_axi_master_ooo) {
+
+               qm->err_ini->close_axi_master_ooo(qm);
+
+       } else if (qm->err_status.is_dev_ecc_mbit &&
+                  !qm->err_status.is_qm_ecc_mbit &&
+                  !qm->err_ini->close_axi_master_ooo) {
+
+               nfe_enb = readl(qm->io_base + QM_RAS_NFE_ENABLE);
+               writel(nfe_enb & QM_RAS_NFE_MBIT_DISABLE,
+                      qm->io_base + QM_RAS_NFE_ENABLE);
+               writel(QM_ECC_MBIT, qm->io_base + QM_ABNORMAL_INT_SET);
+       }
 }
-EXPORT_SYMBOL_GPL(hisi_qm_debug_init);
 
-/**
- * hisi_qm_debug_regs_clear() - clear qm debug related registers.
- * @qm: The qm for which we want to clear its debug registers.
- */
-void hisi_qm_debug_regs_clear(struct hisi_qm *qm)
+static int qm_soft_reset(struct hisi_qm *qm)
 {
-       struct qm_dfx_registers *regs;
-       int i;
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
+       u32 val;
 
-       /* clear current_q */
-       writel(0x0, qm->io_base + QM_DFX_SQE_CNT_VF_SQN);
-       writel(0x0, qm->io_base + QM_DFX_CQE_CNT_VF_CQN);
+       /* Ensure all doorbells and mailboxes received by QM */
+       ret = qm_check_req_recv(qm);
+       if (ret)
+               return ret;
 
-       /*
-        * these registers are reading and clearing, so clear them after
-        * reading them.
-        */
-       writel(0x1, qm->io_base + QM_DFX_CNT_CLR_CE);
+       if (qm->vfs_num) {
+               ret = qm_set_vf_mse(qm, false);
+               if (ret) {
+                       pci_err(pdev, "Fails to disable vf MSE bit.\n");
+                       return ret;
+               }
+       }
 
-       regs = qm_dfx_regs;
-       for (i = 0; i < CNT_CYC_REGS_NUM; i++) {
-               readl(qm->io_base + regs->reg_offset);
-               regs++;
+       ret = qm_set_msi(qm, false);
+       if (ret) {
+               pci_err(pdev, "Fails to disable PEH MSI bit.\n");
+               return ret;
        }
 
-       writel(0x0, qm->io_base + QM_DFX_CNT_CLR_CE);
+       qm_dev_ecc_mbit_handle(qm);
+
+       /* OOO register set and check */
+       writel(ACC_MASTER_GLOBAL_CTRL_SHUTDOWN,
+              qm->io_base + ACC_MASTER_GLOBAL_CTRL);
+
+       /* If bus lock, reset chip */
+       ret = readl_relaxed_poll_timeout(qm->io_base + ACC_MASTER_TRANS_RETURN,
+                                        val,
+                                        (val == ACC_MASTER_TRANS_RETURN_RW),
+                                        POLL_PERIOD, POLL_TIMEOUT);
+       if (ret) {
+               pci_emerg(pdev, "Bus lock! Please reset system.\n");
+               return ret;
+       }
+
+       ret = qm_set_pf_mse(qm, false);
+       if (ret) {
+               pci_err(pdev, "Fails to disable pf MSE bit.\n");
+               return ret;
+       }
+
+       /* The reset related sub-control registers are not in PCI BAR */
+       if (ACPI_HANDLE(&pdev->dev)) {
+               unsigned long long value = 0;
+               acpi_status s;
+
+               s = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev),
+                                         qm->err_ini->err_info.acpi_rst,
+                                         NULL, &value);
+               if (ACPI_FAILURE(s)) {
+                       pci_err(pdev, "NO controller reset method!\n");
+                       return -EIO;
+               }
+
+               if (value) {
+                       pci_err(pdev, "Reset step %llu failed!\n", value);
+                       return -EIO;
+               }
+       } else {
+               pci_err(pdev, "No reset method!\n");
+               return -EINVAL;
+       }
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_debug_regs_clear);
 
-static void qm_hw_error_init(struct hisi_qm *qm)
+static int qm_vf_reset_done(struct hisi_qm *qm)
 {
-       const struct hisi_qm_err_info *err_info = &qm->err_ini->err_info;
+       struct hisi_qm_list *qm_list = qm->qm_list;
+       struct pci_dev *pdev = qm->pdev;
+       struct pci_dev *virtfn;
+       struct hisi_qm *vf_qm;
+       int ret = 0;
 
-       if (!qm->ops->hw_error_init) {
-               dev_err(&qm->pdev->dev, "QM doesn't support hw error handling!\n");
-               return;
+       mutex_lock(&qm_list->lock);
+       list_for_each_entry(vf_qm, &qm_list->list, list) {
+               virtfn = vf_qm->pdev;
+               if (virtfn == pdev)
+                       continue;
+
+               if (pci_physfn(virtfn) == pdev) {
+                       ret = qm_restart(vf_qm);
+                       if (ret)
+                               goto restart_fail;
+               }
        }
 
-       qm->ops->hw_error_init(qm, err_info->ce, err_info->nfe,
-                              err_info->fe, err_info->msi);
+restart_fail:
+       mutex_unlock(&qm_list->lock);
+       return ret;
 }
 
-static void qm_hw_error_uninit(struct hisi_qm *qm)
+static int qm_get_dev_err_status(struct hisi_qm *qm)
 {
-       if (!qm->ops->hw_error_uninit) {
-               dev_err(&qm->pdev->dev, "Unexpected QM hw error uninit!\n");
+       return qm->err_ini->get_dev_hw_err_status(qm);
+}
+
+static int qm_dev_hw_init(struct hisi_qm *qm)
+{
+       return qm->err_ini->hw_init(qm);
+}
+
+static void qm_restart_prepare(struct hisi_qm *qm)
+{
+       u32 value;
+
+       if (!qm->err_status.is_qm_ecc_mbit &&
+           !qm->err_status.is_dev_ecc_mbit)
                return;
-       }
 
-       qm->ops->hw_error_uninit(qm);
+       /* temporarily close the OOO port used for PEH to write out MSI */
+       value = readl(qm->io_base + ACC_AM_CFG_PORT_WR_EN);
+       writel(value & ~qm->err_ini->err_info.msi_wr_port,
+              qm->io_base + ACC_AM_CFG_PORT_WR_EN);
+
+       /* clear dev ecc 2bit error source if having */
+       value = qm_get_dev_err_status(qm) &
+               qm->err_ini->err_info.ecc_2bits_mask;
+       if (value && qm->err_ini->clear_dev_hw_err_status)
+               qm->err_ini->clear_dev_hw_err_status(qm, value);
+
+       /* clear QM ecc mbit error source */
+       writel(QM_ECC_MBIT, qm->io_base + QM_ABNORMAL_INT_SOURCE);
+
+       /* clear AM Reorder Buffer ecc mbit source */
+       writel(ACC_ROB_ECC_ERR_MULTPL, qm->io_base + ACC_AM_ROB_ECC_INT_STS);
+
+       if (qm->err_ini->open_axi_master_ooo)
+               qm->err_ini->open_axi_master_ooo(qm);
 }
 
-static pci_ers_result_t qm_hw_error_handle(struct hisi_qm *qm)
+static void qm_restart_done(struct hisi_qm *qm)
 {
-       if (!qm->ops->hw_error_handle) {
-               dev_err(&qm->pdev->dev, "QM doesn't support hw error report!\n");
-               return PCI_ERS_RESULT_NONE;
+       u32 value;
+
+       if (!qm->err_status.is_qm_ecc_mbit &&
+           !qm->err_status.is_dev_ecc_mbit)
+               return;
+
+       /* open the OOO port for PEH to write out MSI */
+       value = readl(qm->io_base + ACC_AM_CFG_PORT_WR_EN);
+       value |= qm->err_ini->err_info.msi_wr_port;
+       writel(value, qm->io_base + ACC_AM_CFG_PORT_WR_EN);
+
+       qm->err_status.is_qm_ecc_mbit = false;
+       qm->err_status.is_dev_ecc_mbit = false;
+}
+
+static int qm_controller_reset_done(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
+
+       ret = qm_set_msi(qm, true);
+       if (ret) {
+               pci_err(pdev, "Fails to enable PEH MSI bit!\n");
+               return ret;
        }
 
-       return qm->ops->hw_error_handle(qm);
+       ret = qm_set_pf_mse(qm, true);
+       if (ret) {
+               pci_err(pdev, "Fails to enable pf MSE bit!\n");
+               return ret;
+       }
+
+       if (qm->vfs_num) {
+               ret = qm_set_vf_mse(qm, true);
+               if (ret) {
+                       pci_err(pdev, "Fails to enable vf MSE bit!\n");
+                       return ret;
+               }
+       }
+
+       ret = qm_dev_hw_init(qm);
+       if (ret) {
+               pci_err(pdev, "Failed to init device\n");
+               return ret;
+       }
+
+       qm_restart_prepare(qm);
+
+       ret = qm_restart(qm);
+       if (ret) {
+               pci_err(pdev, "Failed to start QM!\n");
+               return ret;
+       }
+
+       if (qm->vfs_num) {
+               ret = qm_vf_q_assign(qm, qm->vfs_num);
+               if (ret) {
+                       pci_err(pdev, "Failed to assign queue!\n");
+                       return ret;
+               }
+       }
+
+       ret = qm_vf_reset_done(qm);
+       if (ret) {
+               pci_err(pdev, "Failed to start VFs!\n");
+               return -EPERM;
+       }
+
+       hisi_qm_dev_err_init(qm);
+       qm_restart_done(qm);
+
+       clear_bit(QM_DEV_RESET_FLAG, &qm->reset_flag);
+
+       return 0;
 }
 
-/**
- * hisi_qm_get_hw_version() - Get hardware version of a qm.
- * @pdev: The device which hardware version we want to get.
- *
- * This function gets the hardware version of a qm. Return QM_HW_UNKNOWN
- * if the hardware version is not supported.
- */
-enum qm_hw_ver hisi_qm_get_hw_version(struct pci_dev *pdev)
+static int qm_controller_reset(struct hisi_qm *qm)
 {
-       switch (pdev->revision) {
-       case QM_HW_V1:
-       case QM_HW_V2:
-               return pdev->revision;
-       default:
-               return QM_HW_UNKNOWN;
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
+
+       pci_info(pdev, "Controller resetting...\n");
+
+       ret = qm_controller_reset_prepare(qm);
+       if (ret)
+               return ret;
+
+       ret = qm_soft_reset(qm);
+       if (ret) {
+               pci_err(pdev, "Controller reset failed (%d)\n", ret);
+               return ret;
        }
+
+       ret = qm_controller_reset_done(qm);
+       if (ret)
+               return ret;
+
+       pci_info(pdev, "Controller reset complete\n");
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_get_hw_version);
 
 /**
- * hisi_qm_dev_err_init() - Initialize device error configuration.
- * @qm: The qm for which we want to do error initialization.
+ * hisi_qm_dev_slot_reset() - slot reset
+ * @pdev: the PCIe device
  *
- * Initialize QM and device error related configuration.
+ * This function offers QM relate PCIe device reset interface. Drivers which
+ * use QM can use this function as slot_reset in its struct pci_error_handlers.
  */
-void hisi_qm_dev_err_init(struct hisi_qm *qm)
+pci_ers_result_t hisi_qm_dev_slot_reset(struct pci_dev *pdev)
 {
-       if (qm->fun_type == QM_HW_VF)
-               return;
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+       int ret;
 
-       qm_hw_error_init(qm);
+       if (pdev->is_virtfn)
+               return PCI_ERS_RESULT_RECOVERED;
 
-       if (!qm->err_ini->hw_err_enable) {
-               dev_err(&qm->pdev->dev, "Device doesn't support hw error init!\n");
-               return;
+       pci_aer_clear_nonfatal_status(pdev);
+
+       /* reset pcie device controller */
+       ret = qm_controller_reset(qm);
+       if (ret) {
+               pci_err(pdev, "Controller reset failed (%d)\n", ret);
+               return PCI_ERS_RESULT_DISCONNECT;
        }
-       qm->err_ini->hw_err_enable(qm);
+
+       return PCI_ERS_RESULT_RECOVERED;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_dev_err_init);
+EXPORT_SYMBOL_GPL(hisi_qm_dev_slot_reset);
 
-/**
- * hisi_qm_dev_err_uninit() - Uninitialize device error configuration.
- * @qm: The qm for which we want to do error uninitialization.
- *
- * Uninitialize QM and device error related configuration.
- */
-void hisi_qm_dev_err_uninit(struct hisi_qm *qm)
+/* check the interrupt is ecc-mbit error or not */
+static int qm_check_dev_error(struct hisi_qm *qm)
 {
+       int ret;
+
        if (qm->fun_type == QM_HW_VF)
-               return;
+               return 0;
+
+       ret = qm_get_hw_error_status(qm) & QM_ECC_MBIT;
+       if (ret)
+               return ret;
+
+       return (qm_get_dev_err_status(qm) &
+               qm->err_ini->err_info.ecc_2bits_mask);
+}
+
+void hisi_qm_reset_prepare(struct pci_dev *pdev)
+{
+       struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+       u32 delay = 0;
+       int ret;
 
-       qm_hw_error_uninit(qm);
+       hisi_qm_dev_err_uninit(pf_qm);
 
-       if (!qm->err_ini->hw_err_disable) {
-               dev_err(&qm->pdev->dev, "Unexpected device hw error uninit!\n");
+       /*
+        * Check whether there is an ECC mbit error, If it occurs, need to
+        * wait for soft reset to fix it.
+        */
+       while (qm_check_dev_error(pf_qm)) {
+               msleep(++delay);
+               if (delay > QM_RESET_WAIT_TIMEOUT)
+                       return;
+       }
+
+       ret = qm_reset_prepare_ready(qm);
+       if (ret) {
+               pci_err(pdev, "FLR not ready!\n");
                return;
        }
-       qm->err_ini->hw_err_disable(qm);
-}
-EXPORT_SYMBOL_GPL(hisi_qm_dev_err_uninit);
 
-/**
- * hisi_qm_free_qps() - free multiple queue pairs.
- * @qps: The queue pairs need to be freed.
- * @qp_num: The num of queue pairs.
- */
-void hisi_qm_free_qps(struct hisi_qp **qps, int qp_num)
-{
-       int i;
+       if (qm->vfs_num) {
+               ret = qm_vf_reset_prepare(qm);
+               if (ret) {
+                       pci_err(pdev, "Failed to prepare reset, ret = %d.\n",
+                               ret);
+                       return;
+               }
+       }
 
-       if (!qps || qp_num <= 0)
+       ret = hisi_qm_stop(qm);
+       if (ret) {
+               pci_err(pdev, "Failed to stop QM, ret = %d.\n", ret);
                return;
+       }
 
-       for (i = qp_num - 1; i >= 0; i--)
-               hisi_qm_release_qp(qps[i]);
+       pci_info(pdev, "FLR resetting...\n");
 }
-EXPORT_SYMBOL_GPL(hisi_qm_free_qps);
+EXPORT_SYMBOL_GPL(hisi_qm_reset_prepare);
 
-static void free_list(struct list_head *head)
+static bool qm_flr_reset_complete(struct pci_dev *pdev)
 {
-       struct hisi_qm_resource *res, *tmp;
+       struct pci_dev *pf_pdev = pci_physfn(pdev);
+       struct hisi_qm *qm = pci_get_drvdata(pf_pdev);
+       u32 id;
 
-       list_for_each_entry_safe(res, tmp, head, list) {
-               list_del(&res->list);
-               kfree(res);
+       pci_read_config_dword(qm->pdev, PCI_COMMAND, &id);
+       if (id == QM_PCI_COMMAND_INVALID) {
+               pci_err(pdev, "Device can not be used!\n");
+               return false;
        }
+
+       clear_bit(QM_DEV_RESET_FLAG, &qm->reset_flag);
+
+       return true;
 }
 
-static int hisi_qm_sort_devices(int node, struct list_head *head,
-                               struct hisi_qm_list *qm_list)
+void hisi_qm_reset_done(struct pci_dev *pdev)
 {
-       struct hisi_qm_resource *res, *tmp;
-       struct hisi_qm *qm;
-       struct list_head *n;
-       struct device *dev;
-       int dev_node = 0;
+       struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));
+       struct hisi_qm *qm = pci_get_drvdata(pdev);
+       int ret;
 
-       list_for_each_entry(qm, &qm_list->list, list) {
-               dev = &qm->pdev->dev;
+       hisi_qm_dev_err_init(pf_qm);
 
-               if (IS_ENABLED(CONFIG_NUMA)) {
-                       dev_node = dev_to_node(dev);
-                       if (dev_node < 0)
-                               dev_node = 0;
+       ret = qm_restart(qm);
+       if (ret) {
+               pci_err(pdev, "Failed to start QM, ret = %d.\n", ret);
+               goto flr_done;
+       }
+
+       if (qm->fun_type == QM_HW_PF) {
+               ret = qm_dev_hw_init(qm);
+               if (ret) {
+                       pci_err(pdev, "Failed to init PF, ret = %d.\n", ret);
+                       goto flr_done;
                }
 
-               res = kzalloc(sizeof(*res), GFP_KERNEL);
-               if (!res)
-                       return -ENOMEM;
+               if (!qm->vfs_num)
+                       goto flr_done;
 
-               res->qm = qm;
-               res->distance = node_distance(dev_node, node);
-               n = head;
-               list_for_each_entry(tmp, head, list) {
-                       if (res->distance < tmp->distance) {
-                               n = &tmp->list;
-                               break;
-                       }
+               ret = qm_vf_q_assign(qm, qm->vfs_num);
+               if (ret) {
+                       pci_err(pdev, "Failed to assign VFs, ret = %d.\n", ret);
+                       goto flr_done;
+               }
+
+               ret = qm_vf_reset_done(qm);
+               if (ret) {
+                       pci_err(pdev, "Failed to start VFs, ret = %d.\n", ret);
+                       goto flr_done;
                }
-               list_add_tail(&res->list, n);
        }
 
-       return 0;
+flr_done:
+       if (qm_flr_reset_complete(pdev))
+               pci_info(pdev, "FLR reset complete\n");
 }
+EXPORT_SYMBOL_GPL(hisi_qm_reset_done);
 
-/**
- * hisi_qm_alloc_qps_node() - Create multiple queue pairs.
- * @qm_list: The list of all available devices.
- * @qp_num: The number of queue pairs need created.
- * @alg_type: The algorithm type.
- * @node: The numa node.
- * @qps: The queue pairs need created.
- *
- * This function will sort all available device according to numa distance.
- * Then try to create all queue pairs from one device, if all devices do
- * not meet the requirements will return error.
- */
-int hisi_qm_alloc_qps_node(struct hisi_qm_list *qm_list, int qp_num,
-                          u8 alg_type, int node, struct hisi_qp **qps)
+static irqreturn_t qm_abnormal_irq(int irq, void *data)
 {
-       struct hisi_qm_resource *tmp;
-       int ret = -ENODEV;
-       LIST_HEAD(head);
-       int i;
+       struct hisi_qm *qm = data;
+       enum acc_err_result ret;
 
-       if (!qps || !qm_list || qp_num <= 0)
-               return -EINVAL;
+       atomic64_inc(&qm->debug.dfx.abnormal_irq_cnt);
+       ret = qm_process_dev_error(qm);
+       if (ret == ACC_ERR_NEED_RESET)
+               schedule_work(&qm->rst_work);
 
-       mutex_lock(&qm_list->lock);
-       if (hisi_qm_sort_devices(node, &head, qm_list)) {
-               mutex_unlock(&qm_list->lock);
-               goto err;
-       }
+       return IRQ_HANDLED;
+}
 
-       list_for_each_entry(tmp, &head, list) {
-               for (i = 0; i < qp_num; i++) {
-                       qps[i] = hisi_qm_create_qp(tmp->qm, alg_type);
-                       if (IS_ERR(qps[i])) {
-                               hisi_qm_free_qps(qps, i);
-                               break;
-                       }
-               }
+static int qm_irq_register(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       int ret;
 
-               if (i == qp_num) {
-                       ret = 0;
-                       break;
+       ret = request_irq(pci_irq_vector(pdev, QM_EQ_EVENT_IRQ_VECTOR),
+                         qm_irq, IRQF_SHARED, qm->dev_name, qm);
+       if (ret)
+               return ret;
+
+       if (qm->ver != QM_HW_V1) {
+               ret = request_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR),
+                                 qm_aeq_irq, IRQF_SHARED, qm->dev_name, qm);
+               if (ret)
+                       goto err_aeq_irq;
+
+               if (qm->fun_type == QM_HW_PF) {
+                       ret = request_irq(pci_irq_vector(pdev,
+                                         QM_ABNORMAL_EVENT_IRQ_VECTOR),
+                                         qm_abnormal_irq, IRQF_SHARED,
+                                         qm->dev_name, qm);
+                       if (ret)
+                               goto err_abonormal_irq;
                }
        }
 
-       mutex_unlock(&qm_list->lock);
-       if (ret)
-               pr_info("Failed to create qps, node[%d], alg[%d], qp[%d]!\n",
-                       node, alg_type, qp_num);
+       return 0;
 
-err:
-       free_list(&head);
+err_abonormal_irq:
+       free_irq(pci_irq_vector(pdev, QM_AEQ_EVENT_IRQ_VECTOR), qm);
+err_aeq_irq:
+       free_irq(pci_irq_vector(pdev, QM_EQ_EVENT_IRQ_VECTOR), qm);
        return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_alloc_qps_node);
 
-static pci_ers_result_t qm_dev_err_handle(struct hisi_qm *qm)
+static void hisi_qm_controller_reset(struct work_struct *rst_work)
 {
-       u32 err_sts;
+       struct hisi_qm *qm = container_of(rst_work, struct hisi_qm, rst_work);
+       int ret;
 
-       if (!qm->err_ini->get_dev_hw_err_status) {
-               dev_err(&qm->pdev->dev, "Device doesn't support get hw error status!\n");
-               return PCI_ERS_RESULT_NONE;
+       /* reset pcie device controller */
+       ret = qm_controller_reset(qm);
+       if (ret)
+               dev_err(&qm->pdev->dev, "controller reset failed (%d)\n", ret);
+
+}
+
+/**
+ * hisi_qm_init() - Initialize configures about qm.
+ * @qm: The qm needing init.
+ *
+ * This function init qm, then we can call hisi_qm_start to put qm into work.
+ */
+int hisi_qm_init(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       struct device *dev = &pdev->dev;
+       unsigned int num_vec;
+       int ret;
+
+       hisi_qm_pre_init(qm);
+
+       ret = qm_alloc_uacce(qm);
+       if (ret < 0)
+               dev_warn(&pdev->dev, "fail to alloc uacce (%d)\n", ret);
+
+       ret = pci_enable_device_mem(pdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to enable device mem!\n");
+               goto err_remove_uacce;
        }
 
-       /* get device hardware error status */
-       err_sts = qm->err_ini->get_dev_hw_err_status(qm);
-       if (err_sts) {
-               if (!qm->err_ini->log_dev_hw_err) {
-                       dev_err(&qm->pdev->dev, "Device doesn't support log hw error!\n");
-                       return PCI_ERS_RESULT_NEED_RESET;
-               }
+       ret = pci_request_mem_regions(pdev, qm->dev_name);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request mem regions!\n");
+               goto err_disable_pcidev;
+       }
 
-               qm->err_ini->log_dev_hw_err(qm, err_sts);
-               return PCI_ERS_RESULT_NEED_RESET;
+       qm->phys_base = pci_resource_start(pdev, PCI_BAR_2);
+       qm->phys_size = pci_resource_len(qm->pdev, PCI_BAR_2);
+       qm->io_base = ioremap(qm->phys_base, qm->phys_size);
+       if (!qm->io_base) {
+               ret = -EIO;
+               goto err_release_mem_regions;
        }
 
-       return PCI_ERS_RESULT_RECOVERED;
-}
+       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+       if (ret < 0)
+               goto err_iounmap;
+       pci_set_master(pdev);
 
-static pci_ers_result_t qm_process_dev_error(struct pci_dev *pdev)
-{
-       struct hisi_qm *qm = pci_get_drvdata(pdev);
-       pci_ers_result_t qm_ret, dev_ret;
+       if (!qm->ops->get_irq_num) {
+               ret = -EOPNOTSUPP;
+               goto err_iounmap;
+       }
+       num_vec = qm->ops->get_irq_num(qm);
+       ret = pci_alloc_irq_vectors(pdev, num_vec, num_vec, PCI_IRQ_MSI);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable MSI vectors!\n");
+               goto err_iounmap;
+       }
 
-       /* log qm error */
-       qm_ret = qm_hw_error_handle(qm);
+       ret = qm_irq_register(qm);
+       if (ret)
+               goto err_free_irq_vectors;
 
-       /* log device error */
-       dev_ret = qm_dev_err_handle(qm);
+       if (qm->fun_type == QM_HW_VF && qm->ver != QM_HW_V1) {
+               /* v2 starts to support get vft by mailbox */
+               ret = hisi_qm_get_vft(qm, &qm->qp_base, &qm->qp_num);
+               if (ret)
+                       goto err_irq_unregister;
+       }
 
-       return (qm_ret == PCI_ERS_RESULT_NEED_RESET ||
-               dev_ret == PCI_ERS_RESULT_NEED_RESET) ?
-               PCI_ERS_RESULT_NEED_RESET : PCI_ERS_RESULT_RECOVERED;
-}
+       ret = hisi_qm_memory_init(qm);
+       if (ret)
+               goto err_irq_unregister;
 
-/**
- * hisi_qm_dev_err_detected() - Get device and qm error status then log it.
- * @pdev: The PCI device which need report error.
- * @state: The connectivity between CPU and device.
- *
- * We register this function into PCIe AER handlers, It will report device or
- * qm hardware error status when error occur.
- */
-pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev,
-                                         pci_channel_state_t state)
-{
-       if (pdev->is_virtfn)
-               return PCI_ERS_RESULT_NONE;
+       INIT_WORK(&qm->work, qm_work_process);
+       if (qm->fun_type == QM_HW_PF)
+               INIT_WORK(&qm->rst_work, hisi_qm_controller_reset);
 
-       pci_info(pdev, "PCI error detected, state(=%d)!!\n", state);
-       if (state == pci_channel_io_perm_failure)
-               return PCI_ERS_RESULT_DISCONNECT;
+       atomic_set(&qm->status.flags, QM_INIT);
+
+       return 0;
 
-       return qm_process_dev_error(pdev);
+err_irq_unregister:
+       qm_irq_unregister(qm);
+err_free_irq_vectors:
+       pci_free_irq_vectors(pdev);
+err_iounmap:
+       iounmap(qm->io_base);
+err_release_mem_regions:
+       pci_release_mem_regions(pdev);
+err_disable_pcidev:
+       pci_disable_device(pdev);
+err_remove_uacce:
+       uacce_remove(qm->uacce);
+       qm->uacce = NULL;
+       return ret;
 }
-EXPORT_SYMBOL_GPL(hisi_qm_dev_err_detected);
+EXPORT_SYMBOL_GPL(hisi_qm_init);
+
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
index ec5b6f48db6c1f98ea20731dc4e22874a99b8f2f..0a351de8d838c71c43b1b4c948774d503dd176cc 100644 (file)
@@ -8,6 +8,10 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 
+#define QM_QNUM_V1                     4096
+#define QM_QNUM_V2                     1024
+#define QM_MAX_VFS_NUM_V2              63
+
 /* qm user domain */
 #define QM_ARUSER_M_CFG_1              0x100088
 #define AXUSER_SNOOP_ENABLE            BIT(30)
@@ -70,7 +74,7 @@
 
 #define QM_BASE_NFE    (QM_AXI_RRESP | QM_AXI_BRESP | QM_ECC_MBIT | \
                         QM_ACC_GET_TASK_TIMEOUT | QM_DB_TIMEOUT | \
-                        QM_OF_FIFO_OF)
+                        QM_OF_FIFO_OF | QM_DB_RANDOM_INVALID)
 #define QM_BASE_CE                     QM_ECC_1BIT
 
 #define QM_Q_DEPTH                     1024
 /* page number for queue file region */
 #define QM_DOORBELL_PAGE_NR            1
 
+enum qm_stop_reason {
+       QM_NORMAL,
+       QM_SOFT_RESET,
+       QM_FLR,
+};
+
+enum qm_state {
+       QM_INIT = 0,
+       QM_START,
+       QM_CLOSE,
+       QM_STOP,
+};
+
 enum qp_state {
+       QP_INIT = 1,
+       QP_START,
        QP_STOP,
+       QP_CLOSE,
 };
 
 enum qm_hw_ver {
        QM_HW_UNKNOWN = -1,
        QM_HW_V1 = 0x20,
        QM_HW_V2 = 0x21,
+       QM_HW_V3 = 0x30,
 };
 
 enum qm_fun_type {
@@ -101,6 +122,14 @@ enum qm_debug_file {
        DEBUG_FILE_NUM,
 };
 
+struct qm_dfx {
+       atomic64_t err_irq_cnt;
+       atomic64_t aeq_irq_cnt;
+       atomic64_t abnormal_irq_cnt;
+       atomic64_t create_qp_err_cnt;
+       atomic64_t mb_err_cnt;
+};
+
 struct debugfs_file {
        enum qm_debug_file index;
        struct mutex lock;
@@ -109,6 +138,9 @@ struct debugfs_file {
 
 struct qm_debug {
        u32 curr_qm_qp_num;
+       u32 sqe_mask_offset;
+       u32 sqe_mask_len;
+       struct qm_dfx dfx;
        struct dentry *debug_root;
        struct dentry *qm_d;
        struct debugfs_file files[DEBUG_FILE_NUM];
@@ -125,22 +157,34 @@ struct hisi_qm_status {
        bool eqc_phase;
        u32 aeq_head;
        bool aeqc_phase;
-       unsigned long flags;
+       atomic_t flags;
+       int stop_reason;
 };
 
 struct hisi_qm;
 
 struct hisi_qm_err_info {
+       char *acpi_rst;
+       u32 msi_wr_port;
+       u32 ecc_2bits_mask;
        u32 ce;
        u32 nfe;
        u32 fe;
-       u32 msi;
+};
+
+struct hisi_qm_err_status {
+       u32 is_qm_ecc_mbit;
+       u32 is_dev_ecc_mbit;
 };
 
 struct hisi_qm_err_ini {
+       int (*hw_init)(struct hisi_qm *qm);
        void (*hw_err_enable)(struct hisi_qm *qm);
        void (*hw_err_disable)(struct hisi_qm *qm);
        u32 (*get_dev_hw_err_status)(struct hisi_qm *qm);
+       void (*clear_dev_hw_err_status)(struct hisi_qm *qm, u32 err_sts);
+       void (*open_axi_master_ooo)(struct hisi_qm *qm);
+       void (*close_axi_master_ooo)(struct hisi_qm *qm);
        void (*log_dev_hw_err)(struct hisi_qm *qm, u32 err_sts);
        struct hisi_qm_err_info err_info;
 };
@@ -161,7 +205,9 @@ struct hisi_qm {
        u32 qp_num;
        u32 qp_in_used;
        u32 ctrl_qp_num;
+       u32 vfs_num;
        struct list_head list;
+       struct hisi_qm_list *qm_list;
 
        struct qm_dma qdma;
        struct qm_sqc *sqc;
@@ -175,10 +221,12 @@ struct hisi_qm {
 
        struct hisi_qm_status status;
        const struct hisi_qm_err_ini *err_ini;
+       struct hisi_qm_err_status err_status;
+       unsigned long reset_flag;
 
-       rwlock_t qps_lock;
-       unsigned long *qp_bitmap;
-       struct hisi_qp **qp_array;
+       struct rw_semaphore qps_lock;
+       struct idr qp_idr;
+       struct hisi_qp *qp_array;
 
        struct mutex mailbox_lock;
 
@@ -187,13 +235,12 @@ struct hisi_qm {
        struct qm_debug debug;
 
        u32 error_mask;
-       u32 msi_mask;
 
        struct workqueue_struct *wq;
        struct work_struct work;
+       struct work_struct rst_work;
 
        const char *algs;
-       bool use_dma_api;
        bool use_sva;
        resource_size_t phys_base;
        resource_size_t phys_size;
@@ -205,7 +252,7 @@ struct hisi_qp_status {
        u16 sq_tail;
        u16 cq_head;
        bool cqc_phase;
-       unsigned long flags;
+       atomic_t flags;
 };
 
 struct hisi_qp_ops {
@@ -230,10 +277,58 @@ struct hisi_qp {
        void (*event_cb)(struct hisi_qp *qp);
 
        struct hisi_qm *qm;
+       bool is_resetting;
        u16 pasid;
        struct uacce_queue *uacce_q;
 };
 
+static inline int q_num_set(const char *val, const struct kernel_param *kp,
+                           unsigned int device)
+{
+       struct pci_dev *pdev = pci_get_device(PCI_VENDOR_ID_HUAWEI,
+                                             device, NULL);
+       u32 n, q_num;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+
+       if (!pdev) {
+               q_num = min_t(u32, QM_QNUM_V1, QM_QNUM_V2);
+               pr_info("No device found currently, suppose queue number is %d\n",
+                       q_num);
+       } else {
+               if (pdev->revision == QM_HW_V1)
+                       q_num = QM_QNUM_V1;
+               else
+                       q_num = QM_QNUM_V2;
+       }
+
+       ret = kstrtou32(val, 10, &n);
+       if (ret || !n || n > q_num)
+               return -EINVAL;
+
+       return param_set_int(val, kp);
+}
+
+static inline int vfs_num_set(const char *val, const struct kernel_param *kp)
+{
+       u32 n;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+
+       ret = kstrtou32(val, 10, &n);
+       if (ret < 0)
+               return ret;
+
+       if (n > QM_MAX_VFS_NUM_V2)
+               return -EINVAL;
+
+       return param_set_int(val, kp);
+}
+
 static inline void hisi_qm_init_list(struct hisi_qm_list *qm_list)
 {
        INIT_LIST_HEAD(&qm_list->list);
@@ -267,14 +362,19 @@ void hisi_qm_release_qp(struct hisi_qp *qp);
 int hisi_qp_send(struct hisi_qp *qp, const void *msg);
 int hisi_qm_get_free_qp_num(struct hisi_qm *qm);
 int hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number);
-int hisi_qm_set_vft(struct hisi_qm *qm, u32 fun_num, u32 base, u32 number);
 int hisi_qm_debug_init(struct hisi_qm *qm);
 enum qm_hw_ver hisi_qm_get_hw_version(struct pci_dev *pdev);
 void hisi_qm_debug_regs_clear(struct hisi_qm *qm);
+int hisi_qm_sriov_enable(struct pci_dev *pdev, int max_vfs);
+int hisi_qm_sriov_disable(struct pci_dev *pdev);
+int hisi_qm_sriov_configure(struct pci_dev *pdev, int num_vfs);
 void hisi_qm_dev_err_init(struct hisi_qm *qm);
 void hisi_qm_dev_err_uninit(struct hisi_qm *qm);
 pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev,
                                          pci_channel_state_t state);
+pci_ers_result_t hisi_qm_dev_slot_reset(struct pci_dev *pdev);
+void hisi_qm_reset_prepare(struct pci_dev *pdev);
+void hisi_qm_reset_done(struct pci_dev *pdev);
 
 struct hisi_acc_sgl_pool;
 struct hisi_acc_hw_sgl *hisi_acc_sg_buf_map_to_hw_sgl(struct device *dev,
index 3598fa17beb28c0621ae914e4c58aa4ae3d6388f..7b64aca704d642208aefa1fd4c22913781a0cdee 100644 (file)
@@ -160,6 +160,10 @@ struct sec_debug_file {
 struct sec_dfx {
        atomic64_t send_cnt;
        atomic64_t recv_cnt;
+       atomic64_t send_busy_cnt;
+       atomic64_t err_bd_cnt;
+       atomic64_t invalid_req_cnt;
+       atomic64_t done_flag_cnt;
 };
 
 struct sec_debug {
@@ -172,7 +176,6 @@ struct sec_dev {
        struct sec_debug debug;
        u32 ctx_q_num;
        bool iommu_used;
-       u32 num_vfs;
        unsigned long status;
 };
 
index 7f1c6a31b82f35360be719233fcbe693ca59782c..64614a9bdf219fa0978fe4f07d00e8c3a8e0293d 100644 (file)
@@ -148,6 +148,7 @@ static int sec_aead_verify(struct sec_req *req)
 static void sec_req_cb(struct hisi_qp *qp, void *resp)
 {
        struct sec_qp_ctx *qp_ctx = qp->qp_ctx;
+       struct sec_dfx *dfx = &qp_ctx->ctx->sec->debug.dfx;
        struct sec_sqe *bd = resp;
        struct sec_ctx *ctx;
        struct sec_req *req;
@@ -157,11 +158,16 @@ static void sec_req_cb(struct hisi_qp *qp, void *resp)
 
        type = bd->type_cipher_auth & SEC_TYPE_MASK;
        if (unlikely(type != SEC_BD_TYPE2)) {
+               atomic64_inc(&dfx->err_bd_cnt);
                pr_err("err bd type [%d]\n", type);
                return;
        }
 
        req = qp_ctx->req_list[le16_to_cpu(bd->type2.tag)];
+       if (unlikely(!req)) {
+               atomic64_inc(&dfx->invalid_req_cnt);
+               return;
+       }
        req->err_type = bd->type2.error_type;
        ctx = req->ctx;
        done = le16_to_cpu(bd->type2.done_flag) & SEC_DONE_MASK;
@@ -174,12 +180,13 @@ static void sec_req_cb(struct hisi_qp *qp, void *resp)
                        "err_type[%d],done[%d],flag[%d]\n",
                        req->err_type, done, flag);
                err = -EIO;
+               atomic64_inc(&dfx->done_flag_cnt);
        }
 
        if (ctx->alg_type == SEC_AEAD && !req->c_req.encrypt)
                err = sec_aead_verify(req);
 
-       atomic64_inc(&ctx->sec->debug.dfx.recv_cnt);
+       atomic64_inc(&dfx->recv_cnt);
 
        ctx->req_op->buf_unmap(ctx, req);
 
@@ -200,10 +207,12 @@ static int sec_bd_send(struct sec_ctx *ctx, struct sec_req *req)
                return -ENOBUFS;
 
        if (!ret) {
-               if (req->fake_busy)
+               if (req->fake_busy) {
+                       atomic64_inc(&ctx->sec->debug.dfx.send_busy_cnt);
                        ret = -EBUSY;
-               else
+               } else {
                        ret = -EINPROGRESS;
+               }
        }
 
        return ret;
@@ -832,7 +841,6 @@ static int sec_aead_auth_set_key(struct sec_auth_ctx *ctx,
                                 struct crypto_authenc_keys *keys)
 {
        struct crypto_shash *hash_tfm = ctx->hash_tfm;
-       SHASH_DESC_ON_STACK(shash, hash_tfm);
        int blocksize, ret;
 
        if (!keys->authkeylen) {
@@ -842,8 +850,8 @@ static int sec_aead_auth_set_key(struct sec_auth_ctx *ctx,
 
        blocksize = crypto_shash_blocksize(hash_tfm);
        if (keys->authkeylen > blocksize) {
-               ret = crypto_shash_digest(shash, keys->authkey,
-                                         keys->authkeylen, ctx->a_key);
+               ret = crypto_shash_tfm_digest(hash_tfm, keys->authkey,
+                                             keys->authkeylen, ctx->a_key);
                if (ret) {
                        pr_err("hisi_sec2: aead auth digest error!\n");
                        return -EINVAL;
index 1f54ebe164b648316b3b01a78f54191391762b25..a4cb58b54b2579787e5cb35a46ebfa60e8fc803d 100644 (file)
@@ -80,6 +80,9 @@
 #define SEC_VF_CNT_MASK                        0xffffffc0
 #define SEC_DBGFS_VAL_MAX_LEN          20
 
+#define SEC_SQE_MASK_OFFSET            64
+#define SEC_SQE_MASK_LEN               48
+
 #define SEC_ADDR(qm, offset) ((qm)->io_base + (offset) + \
                             SEC_ENGINE_PF_CFG_OFF + SEC_ACC_COMMON_REG_OFF)
 
@@ -88,6 +91,11 @@ struct sec_hw_error {
        const char *msg;
 };
 
+struct sec_dfx_item {
+       const char *name;
+       u32 offset;
+};
+
 static const char sec_name[] = "hisi_sec2";
 static struct dentry *sec_debugfs_root;
 static struct hisi_qm_list sec_devices;
@@ -110,7 +118,16 @@ static const char * const sec_dbg_file_name[] = {
        [SEC_CLEAR_ENABLE] = "clear_enable",
 };
 
-static struct debugfs_reg32 sec_dfx_regs[] = {
+static struct sec_dfx_item sec_dfx_labels[] = {
+       {"send_cnt", offsetof(struct sec_dfx, send_cnt)},
+       {"recv_cnt", offsetof(struct sec_dfx, recv_cnt)},
+       {"send_busy_cnt", offsetof(struct sec_dfx, send_busy_cnt)},
+       {"err_bd_cnt", offsetof(struct sec_dfx, err_bd_cnt)},
+       {"invalid_req_cnt", offsetof(struct sec_dfx, invalid_req_cnt)},
+       {"done_flag_cnt", offsetof(struct sec_dfx, done_flag_cnt)},
+};
+
+static const struct debugfs_reg32 sec_dfx_regs[] = {
        {"SEC_PF_ABNORMAL_INT_SOURCE    ",  0x301010},
        {"SEC_SAA_EN                    ",  0x301270},
        {"SEC_BD_LATENCY_MIN            ",  0x301600},
@@ -136,45 +153,14 @@ static struct debugfs_reg32 sec_dfx_regs[] = {
 
 static int sec_pf_q_num_set(const char *val, const struct kernel_param *kp)
 {
-       struct pci_dev *pdev;
-       u32 n, q_num;
-       u8 rev_id;
-       int ret;
-
-       if (!val)
-               return -EINVAL;
-
-       pdev = pci_get_device(PCI_VENDOR_ID_HUAWEI,
-                             SEC_PF_PCI_DEVICE_ID, NULL);
-       if (!pdev) {
-               q_num = min_t(u32, SEC_QUEUE_NUM_V1, SEC_QUEUE_NUM_V2);
-               pr_info("No device, suppose queue number is %d!\n", q_num);
-       } else {
-               rev_id = pdev->revision;
-
-               switch (rev_id) {
-               case QM_HW_V1:
-                       q_num = SEC_QUEUE_NUM_V1;
-                       break;
-               case QM_HW_V2:
-                       q_num = SEC_QUEUE_NUM_V2;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       ret = kstrtou32(val, 10, &n);
-       if (ret || !n || n > q_num)
-               return -EINVAL;
-
-       return param_set_int(val, kp);
+       return q_num_set(val, kp, SEC_PF_PCI_DEVICE_ID);
 }
 
 static const struct kernel_param_ops sec_pf_q_num_ops = {
        .set = sec_pf_q_num_set,
        .get = param_get_int,
 };
+
 static u32 pf_q_num = SEC_PF_DEF_Q_NUM;
 module_param_cb(pf_q_num, &sec_pf_q_num_ops, &pf_q_num, 0444);
 MODULE_PARM_DESC(pf_q_num, "Number of queues in PF(v1 0-4096, v2 0-1024)");
@@ -207,6 +193,15 @@ static u32 ctx_q_num = SEC_CTX_Q_NUM_DEF;
 module_param_cb(ctx_q_num, &sec_ctx_q_num_ops, &ctx_q_num, 0444);
 MODULE_PARM_DESC(ctx_q_num, "Queue num in ctx (24 default, 2, 4, ..., 32)");
 
+static const struct kernel_param_ops vfs_num_ops = {
+       .set = vfs_num_set,
+       .get = param_get_int,
+};
+
+static u32 vfs_num;
+module_param_cb(vfs_num, &vfs_num_ops, &vfs_num, 0444);
+MODULE_PARM_DESC(vfs_num, "Number of VFs to enable(1-63), 0(default)");
+
 void sec_destroy_qps(struct hisi_qp **qps, int qp_num)
 {
        hisi_qm_free_qps(qps, qp_num);
@@ -240,9 +235,8 @@ static const struct pci_device_id sec_dev_ids[] = {
 };
 MODULE_DEVICE_TABLE(pci, sec_dev_ids);
 
-static u8 sec_get_endian(struct sec_dev *sec)
+static u8 sec_get_endian(struct hisi_qm *qm)
 {
-       struct hisi_qm *qm = &sec->qm;
        u32 reg;
 
        /*
@@ -270,9 +264,8 @@ static u8 sec_get_endian(struct sec_dev *sec)
                return SEC_64BE;
 }
 
-static int sec_engine_init(struct sec_dev *sec)
+static int sec_engine_init(struct hisi_qm *qm)
 {
-       struct hisi_qm *qm = &sec->qm;
        int ret;
        u32 reg;
 
@@ -315,7 +308,7 @@ static int sec_engine_init(struct sec_dev *sec)
 
        /* config endian */
        reg = readl_relaxed(SEC_ADDR(qm, SEC_CONTROL_REG));
-       reg |= sec_get_endian(sec);
+       reg |= sec_get_endian(qm);
        writel_relaxed(reg, SEC_ADDR(qm, SEC_CONTROL_REG));
 
        /* Enable sm4 xts mode multiple iv */
@@ -325,10 +318,8 @@ static int sec_engine_init(struct sec_dev *sec)
        return 0;
 }
 
-static int sec_set_user_domain_and_cache(struct sec_dev *sec)
+static int sec_set_user_domain_and_cache(struct hisi_qm *qm)
 {
-       struct hisi_qm *qm = &sec->qm;
-
        /* qm user domain */
        writel(AXUSER_BASE, qm->io_base + QM_ARUSER_M_CFG_1);
        writel(ARUSER_M_CFG_ENABLE, qm->io_base + QM_ARUSER_M_CFG_ENABLE);
@@ -349,7 +340,7 @@ static int sec_set_user_domain_and_cache(struct sec_dev *sec)
               CQC_CACHE_WB_ENABLE | FIELD_PREP(SQC_CACHE_WB_THRD, 1) |
               FIELD_PREP(CQC_CACHE_WB_THRD, 1), qm->io_base + QM_CACHE_CTL);
 
-       return sec_engine_init(sec);
+       return sec_engine_init(qm);
 }
 
 /* sec_debug_regs_clear() - clear the sec debug regs */
@@ -424,23 +415,22 @@ static u32 sec_current_qm_read(struct sec_debug_file *file)
 static int sec_current_qm_write(struct sec_debug_file *file, u32 val)
 {
        struct hisi_qm *qm = file->qm;
-       struct sec_dev *sec = container_of(qm, struct sec_dev, qm);
        u32 vfq_num;
        u32 tmp;
 
-       if (val > sec->num_vfs)
+       if (val > qm->vfs_num)
                return -EINVAL;
 
        /* According PF or VF Dev ID to calculation curr_qm_qp_num and store */
        if (!val) {
                qm->debug.curr_qm_qp_num = qm->qp_num;
        } else {
-               vfq_num = (qm->ctrl_qp_num - qm->qp_num) / sec->num_vfs;
+               vfq_num = (qm->ctrl_qp_num - qm->qp_num) / qm->vfs_num;
 
-               if (val == sec->num_vfs)
+               if (val == qm->vfs_num)
                        qm->debug.curr_qm_qp_num =
                                qm->ctrl_qp_num - qm->qp_num -
-                               (sec->num_vfs - 1) * vfq_num;
+                               (qm->vfs_num - 1) * vfq_num;
                else
                        qm->debug.curr_qm_qp_num = vfq_num;
        }
@@ -570,10 +560,22 @@ static const struct file_operations sec_dbg_fops = {
 static int sec_debugfs_atomic64_get(void *data, u64 *val)
 {
        *val = atomic64_read((atomic64_t *)data);
+
+       return 0;
+}
+
+static int sec_debugfs_atomic64_set(void *data, u64 val)
+{
+       if (val)
+               return -EINVAL;
+
+       atomic64_set((atomic64_t *)data, 0);
+
        return 0;
 }
+
 DEFINE_DEBUGFS_ATTRIBUTE(sec_atomic64_ops, sec_debugfs_atomic64_get,
-                        NULL, "%lld\n");
+                        sec_debugfs_atomic64_set, "%lld\n");
 
 static int sec_core_debug_init(struct sec_dev *sec)
 {
@@ -582,6 +584,7 @@ static int sec_core_debug_init(struct sec_dev *sec)
        struct sec_dfx *dfx = &sec->debug.dfx;
        struct debugfs_regset32 *regset;
        struct dentry *tmp_d;
+       int i;
 
        tmp_d = debugfs_create_dir("sec_dfx", sec->qm.debug.debug_root);
 
@@ -593,13 +596,15 @@ static int sec_core_debug_init(struct sec_dev *sec)
        regset->nregs = ARRAY_SIZE(sec_dfx_regs);
        regset->base = qm->io_base;
 
-       debugfs_create_regset32("regs", 0444, tmp_d, regset);
-
-       debugfs_create_file("send_cnt", 0444, tmp_d,
-                           &dfx->send_cnt, &sec_atomic64_ops);
+       if (qm->pdev->device == SEC_PF_PCI_DEVICE_ID)
+               debugfs_create_regset32("regs", 0444, tmp_d, regset);
 
-       debugfs_create_file("recv_cnt", 0444, tmp_d,
-                           &dfx->recv_cnt, &sec_atomic64_ops);
+       for (i = 0; i < ARRAY_SIZE(sec_dfx_labels); i++) {
+               atomic64_t *data = (atomic64_t *)((uintptr_t)dfx +
+                                       sec_dfx_labels[i].offset);
+               debugfs_create_file(sec_dfx_labels[i].name, 0644,
+                                  tmp_d, data, &sec_atomic64_ops);
+       }
 
        return 0;
 }
@@ -630,6 +635,9 @@ static int sec_debugfs_init(struct sec_dev *sec)
 
        qm->debug.debug_root = debugfs_create_dir(dev_name(dev),
                                                  sec_debugfs_root);
+
+       qm->debug.sqe_mask_offset = SEC_SQE_MASK_OFFSET;
+       qm->debug.sqe_mask_len = SEC_SQE_MASK_LEN;
        ret = hisi_qm_debug_init(qm);
        if (ret)
                goto failed_to_create;
@@ -675,8 +683,6 @@ static void sec_log_hw_error(struct hisi_qm *qm, u32 err_sts)
                }
                errs++;
        }
-
-       writel(err_sts, qm->io_base + SEC_CORE_INT_SOURCE);
 }
 
 static u32 sec_get_hw_err_status(struct hisi_qm *qm)
@@ -684,17 +690,36 @@ static u32 sec_get_hw_err_status(struct hisi_qm *qm)
        return readl(qm->io_base + SEC_CORE_INT_STATUS);
 }
 
+static void sec_clear_hw_err_status(struct hisi_qm *qm, u32 err_sts)
+{
+       writel(err_sts, qm->io_base + SEC_CORE_INT_SOURCE);
+}
+
+static void sec_open_axi_master_ooo(struct hisi_qm *qm)
+{
+       u32 val;
+
+       val = readl(SEC_ADDR(qm, SEC_CONTROL_REG));
+       writel(val & SEC_AXI_SHUTDOWN_DISABLE, SEC_ADDR(qm, SEC_CONTROL_REG));
+       writel(val | SEC_AXI_SHUTDOWN_ENABLE, SEC_ADDR(qm, SEC_CONTROL_REG));
+}
+
 static const struct hisi_qm_err_ini sec_err_ini = {
+       .hw_init                = sec_set_user_domain_and_cache,
        .hw_err_enable          = sec_hw_error_enable,
        .hw_err_disable         = sec_hw_error_disable,
        .get_dev_hw_err_status  = sec_get_hw_err_status,
+       .clear_dev_hw_err_status = sec_clear_hw_err_status,
        .log_dev_hw_err         = sec_log_hw_error,
+       .open_axi_master_ooo    = sec_open_axi_master_ooo,
        .err_info               = {
                .ce                     = QM_BASE_CE,
                .nfe                    = QM_BASE_NFE | QM_ACC_DO_TASK_TIMEOUT |
                                          QM_ACC_WB_NOT_READY_TIMEOUT,
                .fe                     = 0,
-               .msi                    = QM_DB_RANDOM_INVALID,
+               .ecc_2bits_mask         = SEC_CORE_INT_STATUS_M_ECC,
+               .msi_wr_port            = BIT(0),
+               .acpi_rst               = "SRST",
        }
 };
 
@@ -703,22 +728,14 @@ static int sec_pf_probe_init(struct sec_dev *sec)
        struct hisi_qm *qm = &sec->qm;
        int ret;
 
-       switch (qm->ver) {
-       case QM_HW_V1:
+       if (qm->ver == QM_HW_V1)
                qm->ctrl_qp_num = SEC_QUEUE_NUM_V1;
-               break;
-
-       case QM_HW_V2:
+       else
                qm->ctrl_qp_num = SEC_QUEUE_NUM_V2;
-               break;
-
-       default:
-               return -EINVAL;
-       }
 
        qm->err_ini = &sec_err_ini;
 
-       ret = sec_set_user_domain_and_cache(sec);
+       ret = sec_set_user_domain_and_cache(qm);
        if (ret)
                return ret;
 
@@ -730,32 +747,30 @@ static int sec_pf_probe_init(struct sec_dev *sec)
 
 static int sec_qm_init(struct hisi_qm *qm, struct pci_dev *pdev)
 {
-       enum qm_hw_ver rev_id;
-
-       rev_id = hisi_qm_get_hw_version(pdev);
-       if (rev_id == QM_HW_UNKNOWN)
-               return -ENODEV;
+       int ret;
 
        qm->pdev = pdev;
-       qm->ver = rev_id;
-
+       qm->ver = pdev->revision;
        qm->sqe_size = SEC_SQE_SIZE;
        qm->dev_name = sec_name;
+
        qm->fun_type = (pdev->device == SEC_PF_PCI_DEVICE_ID) ?
                        QM_HW_PF : QM_HW_VF;
-       qm->use_dma_api = true;
-
-       return hisi_qm_init(qm);
-}
-
-static void sec_qm_uninit(struct hisi_qm *qm)
-{
-       hisi_qm_uninit(qm);
-}
-
-static int sec_probe_init(struct hisi_qm *qm, struct sec_dev *sec)
-{
-       int ret;
+       if (qm->fun_type == QM_HW_PF) {
+               qm->qp_base = SEC_PF_DEF_Q_BASE;
+               qm->qp_num = pf_q_num;
+               qm->debug.curr_qm_qp_num = pf_q_num;
+               qm->qm_list = &sec_devices;
+       } else if (qm->fun_type == QM_HW_VF && qm->ver == QM_HW_V1) {
+               /*
+                * have no way to get qm configure in VM in v1 hardware,
+                * so currently force PF to uses SEC_PF_DEF_Q_NUM, and force
+                * to trigger only one VF in v1 hardware.
+                * v2 hardware has no such problem.
+                */
+               qm->qp_base = SEC_PF_DEF_Q_NUM;
+               qm->qp_num = SEC_QUEUE_NUM_V1 - SEC_PF_DEF_Q_NUM;
+       }
 
        /*
         * WQ_HIGHPRI: SEC request must be low delayed,
@@ -763,47 +778,38 @@ static int sec_probe_init(struct hisi_qm *qm, struct sec_dev *sec)
         * WQ_UNBOUND: SEC task is likely with long
         * running CPU intensive workloads.
         */
-       qm->wq = alloc_workqueue("%s", WQ_HIGHPRI |
-               WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus(),
-               pci_name(qm->pdev));
+       qm->wq = alloc_workqueue("%s", WQ_HIGHPRI | WQ_MEM_RECLAIM |
+                                WQ_UNBOUND, num_online_cpus(),
+                                pci_name(qm->pdev));
        if (!qm->wq) {
                pci_err(qm->pdev, "fail to alloc workqueue\n");
                return -ENOMEM;
        }
 
-       if (qm->fun_type == QM_HW_PF) {
-               qm->qp_base = SEC_PF_DEF_Q_BASE;
-               qm->qp_num = pf_q_num;
-               qm->debug.curr_qm_qp_num = pf_q_num;
+       ret = hisi_qm_init(qm);
+       if (ret)
+               destroy_workqueue(qm->wq);
+
+       return ret;
+}
 
+static void sec_qm_uninit(struct hisi_qm *qm)
+{
+       hisi_qm_uninit(qm);
+}
+
+static int sec_probe_init(struct sec_dev *sec)
+{
+       struct hisi_qm *qm = &sec->qm;
+       int ret;
+
+       if (qm->fun_type == QM_HW_PF) {
                ret = sec_pf_probe_init(sec);
                if (ret)
-                       goto err_probe_uninit;
-       } else if (qm->fun_type == QM_HW_VF) {
-               /*
-                * have no way to get qm configure in VM in v1 hardware,
-                * so currently force PF to uses SEC_PF_DEF_Q_NUM, and force
-                * to trigger only one VF in v1 hardware.
-                * v2 hardware has no such problem.
-                */
-               if (qm->ver == QM_HW_V1) {
-                       qm->qp_base = SEC_PF_DEF_Q_NUM;
-                       qm->qp_num = SEC_QUEUE_NUM_V1 - SEC_PF_DEF_Q_NUM;
-               } else if (qm->ver == QM_HW_V2) {
-                       /* v2 starts to support get vft by mailbox */
-                       ret = hisi_qm_get_vft(qm, &qm->qp_base, &qm->qp_num);
-                       if (ret)
-                               goto err_probe_uninit;
-               }
-       } else {
-               ret = -ENODEV;
-               goto err_probe_uninit;
+                       return ret;
        }
 
        return 0;
-err_probe_uninit:
-       destroy_workqueue(qm->wq);
-       return ret;
 }
 
 static void sec_probe_uninit(struct hisi_qm *qm)
@@ -840,20 +846,17 @@ static int sec_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (!sec)
                return -ENOMEM;
 
-       pci_set_drvdata(pdev, sec);
-
-       sec->ctx_q_num = ctx_q_num;
-       sec_iommu_used_check(sec);
-
        qm = &sec->qm;
-
        ret = sec_qm_init(qm, pdev);
        if (ret) {
-               pci_err(pdev, "Failed to pre init qm!\n");
+               pci_err(pdev, "Failed to init SEC QM (%d)!\n", ret);
                return ret;
        }
 
-       ret = sec_probe_init(qm, sec);
+       sec->ctx_q_num = ctx_q_num;
+       sec_iommu_used_check(sec);
+
+       ret = sec_probe_init(sec);
        if (ret) {
                pci_err(pdev, "Failed to probe!\n");
                goto err_qm_uninit;
@@ -877,8 +880,17 @@ static int sec_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_remove_from_list;
        }
 
+       if (qm->fun_type == QM_HW_PF && vfs_num) {
+               ret = hisi_qm_sriov_enable(pdev, vfs_num);
+               if (ret < 0)
+                       goto err_crypto_unregister;
+       }
+
        return 0;
 
+err_crypto_unregister:
+       sec_unregister_from_crypto();
+
 err_remove_from_list:
        hisi_qm_del_from_list(qm, &sec_devices);
        sec_debugfs_exit(sec);
@@ -893,110 +905,6 @@ err_qm_uninit:
        return ret;
 }
 
-/* now we only support equal assignment */
-static int sec_vf_q_assign(struct sec_dev *sec, u32 num_vfs)
-{
-       struct hisi_qm *qm = &sec->qm;
-       u32 qp_num = qm->qp_num;
-       u32 q_base = qp_num;
-       u32 q_num, remain_q_num;
-       int i, j, ret;
-
-       if (!num_vfs)
-               return -EINVAL;
-
-       remain_q_num = qm->ctrl_qp_num - qp_num;
-       q_num = remain_q_num / num_vfs;
-
-       for (i = 1; i <= num_vfs; i++) {
-               if (i == num_vfs)
-                       q_num += remain_q_num % num_vfs;
-               ret = hisi_qm_set_vft(qm, i, q_base, q_num);
-               if (ret) {
-                       for (j = i; j > 0; j--)
-                               hisi_qm_set_vft(qm, j, 0, 0);
-                       return ret;
-               }
-               q_base += q_num;
-       }
-
-       return 0;
-}
-
-static int sec_clear_vft_config(struct sec_dev *sec)
-{
-       struct hisi_qm *qm = &sec->qm;
-       u32 num_vfs = sec->num_vfs;
-       int ret;
-       u32 i;
-
-       for (i = 1; i <= num_vfs; i++) {
-               ret = hisi_qm_set_vft(qm, i, 0, 0);
-               if (ret)
-                       return ret;
-       }
-
-       sec->num_vfs = 0;
-
-       return 0;
-}
-
-static int sec_sriov_enable(struct pci_dev *pdev, int max_vfs)
-{
-       struct sec_dev *sec = pci_get_drvdata(pdev);
-       int pre_existing_vfs, ret;
-       u32 num_vfs;
-
-       pre_existing_vfs = pci_num_vf(pdev);
-
-       if (pre_existing_vfs) {
-               pci_err(pdev, "Can't enable VF. Please disable at first!\n");
-               return 0;
-       }
-
-       num_vfs = min_t(u32, max_vfs, SEC_VF_NUM);
-
-       ret = sec_vf_q_assign(sec, num_vfs);
-       if (ret) {
-               pci_err(pdev, "Can't assign queues for VF!\n");
-               return ret;
-       }
-
-       sec->num_vfs = num_vfs;
-
-       ret = pci_enable_sriov(pdev, num_vfs);
-       if (ret) {
-               pci_err(pdev, "Can't enable VF!\n");
-               sec_clear_vft_config(sec);
-               return ret;
-       }
-
-       return num_vfs;
-}
-
-static int sec_sriov_disable(struct pci_dev *pdev)
-{
-       struct sec_dev *sec = pci_get_drvdata(pdev);
-
-       if (pci_vfs_assigned(pdev)) {
-               pci_err(pdev, "Can't disable VFs while VFs are assigned!\n");
-               return -EPERM;
-       }
-
-       /* remove in sec_pci_driver will be called to free VF resources */
-       pci_disable_sriov(pdev);
-
-       return sec_clear_vft_config(sec);
-}
-
-static int sec_sriov_configure(struct pci_dev *pdev, int num_vfs)
-{
-       if (num_vfs)
-               return sec_sriov_enable(pdev, num_vfs);
-       else
-               return sec_sriov_disable(pdev);
-}
-
 static void sec_remove(struct pci_dev *pdev)
 {
        struct sec_dev *sec = pci_get_drvdata(pdev);
@@ -1006,8 +914,8 @@ static void sec_remove(struct pci_dev *pdev)
 
        hisi_qm_del_from_list(qm, &sec_devices);
 
-       if (qm->fun_type == QM_HW_PF && sec->num_vfs)
-               (void)sec_sriov_disable(pdev);
+       if (qm->fun_type == QM_HW_PF && qm->vfs_num)
+               hisi_qm_sriov_disable(pdev);
 
        sec_debugfs_exit(sec);
 
@@ -1023,6 +931,9 @@ static void sec_remove(struct pci_dev *pdev)
 
 static const struct pci_error_handlers sec_err_handler = {
        .error_detected = hisi_qm_dev_err_detected,
+       .slot_reset =  hisi_qm_dev_slot_reset,
+       .reset_prepare          = hisi_qm_reset_prepare,
+       .reset_done             = hisi_qm_reset_done,
 };
 
 static struct pci_driver sec_pci_driver = {
@@ -1031,7 +942,7 @@ static struct pci_driver sec_pci_driver = {
        .probe = sec_probe,
        .remove = sec_remove,
        .err_handler = &sec_err_handler,
-       .sriov_configure = sec_sriov_configure,
+       .sriov_configure = hisi_qm_sriov_configure,
 };
 
 static void sec_register_debugfs(void)
index 82dc6f867171a649b3f3df071858dde30617113c..f3ed4c0e54934b7e7f639a2a9fdd8f0a1aa9d15d 100644 (file)
@@ -28,12 +28,20 @@ enum hisi_zip_error_type {
        HZIP_NC_ERR = 0x0d,
 };
 
+struct hisi_zip_dfx {
+       atomic64_t send_cnt;
+       atomic64_t recv_cnt;
+       atomic64_t send_busy_cnt;
+       atomic64_t err_bd_cnt;
+};
+
 struct hisi_zip_ctrl;
 
 struct hisi_zip {
        struct hisi_qm qm;
        struct list_head list;
        struct hisi_zip_ctrl *ctrl;
+       struct hisi_zip_dfx dfx;
 };
 
 struct hisi_zip_sqe {
index 369ec32205742862807aeaeb2ef7a16e86b34b20..c73707c2e5394d67e8df50202040556a2b55f08e 100644 (file)
@@ -64,7 +64,6 @@ struct hisi_zip_req_q {
 
 struct hisi_zip_qp_ctx {
        struct hisi_qp *qp;
-       struct hisi_zip_sqe zip_sqe;
        struct hisi_zip_req_q req_q;
        struct hisi_acc_sgl_pool *sgl_pool;
        struct hisi_zip *zip_dev;
@@ -333,6 +332,7 @@ static void hisi_zip_acomp_cb(struct hisi_qp *qp, void *data)
 {
        struct hisi_zip_sqe *sqe = data;
        struct hisi_zip_qp_ctx *qp_ctx = qp->qp_ctx;
+       struct hisi_zip_dfx *dfx = &qp_ctx->zip_dev->dfx;
        struct hisi_zip_req_q *req_q = &qp_ctx->req_q;
        struct hisi_zip_req *req = req_q->q + sqe->tag;
        struct acomp_req *acomp_req = req->req;
@@ -340,12 +340,14 @@ static void hisi_zip_acomp_cb(struct hisi_qp *qp, void *data)
        u32 status, dlen, head_size;
        int err = 0;
 
+       atomic64_inc(&dfx->recv_cnt);
        status = sqe->dw3 & HZIP_BD_STATUS_M;
 
        if (status != 0 && status != HZIP_NC_ERR) {
                dev_err(dev, "%scompress fail in qp%u: %u, output: %u\n",
                        (qp->alg_type == 0) ? "" : "de", qp->qp_id, status,
                        sqe->produced);
+               atomic64_inc(&dfx->err_bd_cnt);
                err = -EIO;
        }
        dlen = sqe->produced;
@@ -484,11 +486,12 @@ static struct hisi_zip_req *hisi_zip_create_req(struct acomp_req *req,
 static int hisi_zip_do_work(struct hisi_zip_req *req,
                            struct hisi_zip_qp_ctx *qp_ctx)
 {
-       struct hisi_zip_sqe *zip_sqe = &qp_ctx->zip_sqe;
        struct acomp_req *a_req = req->req;
        struct hisi_qp *qp = qp_ctx->qp;
        struct device *dev = &qp->qm->pdev->dev;
        struct hisi_acc_sgl_pool *pool = qp_ctx->sgl_pool;
+       struct hisi_zip_dfx *dfx = &qp_ctx->zip_dev->dfx;
+       struct hisi_zip_sqe zip_sqe;
        dma_addr_t input;
        dma_addr_t output;
        int ret;
@@ -511,15 +514,18 @@ static int hisi_zip_do_work(struct hisi_zip_req *req,
        }
        req->dma_dst = output;
 
-       hisi_zip_fill_sqe(zip_sqe, qp->req_type, input, output, a_req->slen,
+       hisi_zip_fill_sqe(&zip_sqe, qp->req_type, input, output, a_req->slen,
                          a_req->dlen, req->sskip, req->dskip);
-       hisi_zip_config_buf_type(zip_sqe, HZIP_SGL);
-       hisi_zip_config_tag(zip_sqe, req->req_id);
+       hisi_zip_config_buf_type(&zip_sqe, HZIP_SGL);
+       hisi_zip_config_tag(&zip_sqe, req->req_id);
 
        /* send command to start a task */
-       ret = hisi_qp_send(qp, zip_sqe);
-       if (ret < 0)
+       atomic64_inc(&dfx->send_cnt);
+       ret = hisi_qp_send(qp, &zip_sqe);
+       if (ret < 0) {
+               atomic64_inc(&dfx->send_busy_cnt);
                goto err_unmap_output;
+       }
 
        return -EINPROGRESS;
 
index fcc85d2dbd0720031dfa3318176d6280a0bf1a0f..2229a21ae7c88a71b790333b45d712c81200a9e8 100644 (file)
@@ -62,6 +62,7 @@
 
 #define HZIP_CORE_INT_SOURCE           0x3010A0
 #define HZIP_CORE_INT_MASK_REG         0x3010A4
+#define HZIP_CORE_INT_SET              0x3010A8
 #define HZIP_CORE_INT_STATUS           0x3010AC
 #define HZIP_CORE_INT_STATUS_M_ECC     BIT(1)
 #define HZIP_CORE_SRAM_ECC_ERR_INFO    0x301148
 
 #define HZIP_SOFT_CTRL_CNT_CLR_CE      0x301000
 #define SOFT_CTRL_CNT_CLR_CE_BIT       BIT(0)
+#define HZIP_SOFT_CTRL_ZIP_CONTROL     0x30100C
+#define HZIP_AXI_SHUTDOWN_ENABLE       BIT(14)
+#define HZIP_WR_PORT                   BIT(11)
 
 #define HZIP_BUF_SIZE                  22
+#define HZIP_SQE_MASK_OFFSET           64
+#define HZIP_SQE_MASK_LEN              48
 
 static const char hisi_zip_name[] = "hisi_zip";
 static struct dentry *hzip_debugfs_root;
@@ -95,6 +101,18 @@ struct hisi_zip_hw_error {
        const char *msg;
 };
 
+struct zip_dfx_item {
+       const char *name;
+       u32 offset;
+};
+
+static struct zip_dfx_item zip_dfx_files[] = {
+       {"send_cnt", offsetof(struct hisi_zip_dfx, send_cnt)},
+       {"recv_cnt", offsetof(struct hisi_zip_dfx, recv_cnt)},
+       {"send_busy_cnt", offsetof(struct hisi_zip_dfx, send_busy_cnt)},
+       {"err_bd_cnt", offsetof(struct hisi_zip_dfx, err_bd_cnt)},
+};
+
 static const struct hisi_zip_hw_error zip_hw_error[] = {
        { .int_msk = BIT(0), .msg = "zip_ecc_1bitt_err" },
        { .int_msk = BIT(1), .msg = "zip_ecc_2bit_err" },
@@ -134,7 +152,6 @@ struct ctrl_debug_file {
  * Just relevant for PF.
  */
 struct hisi_zip_ctrl {
-       u32 num_vfs;
        struct hisi_zip *hisi_zip;
        struct dentry *debug_root;
        struct ctrl_debug_file files[HZIP_DEBUG_FILE_NUM];
@@ -162,7 +179,7 @@ static const u64 core_offsets[] = {
        [HZIP_DECOMP_CORE5] = 0x309000,
 };
 
-static struct debugfs_reg32 hzip_dfx_regs[] = {
+static const struct debugfs_reg32 hzip_dfx_regs[] = {
        {"HZIP_GET_BD_NUM                ",  0x00ull},
        {"HZIP_GET_RIGHT_BD              ",  0x04ull},
        {"HZIP_GET_ERROR_BD              ",  0x08ull},
@@ -189,38 +206,7 @@ static struct debugfs_reg32 hzip_dfx_regs[] = {
 
 static int pf_q_num_set(const char *val, const struct kernel_param *kp)
 {
-       struct pci_dev *pdev = pci_get_device(PCI_VENDOR_ID_HUAWEI,
-                                             PCI_DEVICE_ID_ZIP_PF, NULL);
-       u32 n, q_num;
-       u8 rev_id;
-       int ret;
-
-       if (!val)
-               return -EINVAL;
-
-       if (!pdev) {
-               q_num = min_t(u32, HZIP_QUEUE_NUM_V1, HZIP_QUEUE_NUM_V2);
-               pr_info("No device found currently, suppose queue number is %d\n",
-                       q_num);
-       } else {
-               rev_id = pdev->revision;
-               switch (rev_id) {
-               case QM_HW_V1:
-                       q_num = HZIP_QUEUE_NUM_V1;
-                       break;
-               case QM_HW_V2:
-                       q_num = HZIP_QUEUE_NUM_V2;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       ret = kstrtou32(val, 10, &n);
-       if (ret != 0 || n > q_num || n == 0)
-               return -EINVAL;
-
-       return param_set_int(val, kp);
+       return q_num_set(val, kp, PCI_DEVICE_ID_ZIP_PF);
 }
 
 static const struct kernel_param_ops pf_q_num_ops = {
@@ -232,9 +218,14 @@ static u32 pf_q_num = HZIP_PF_DEF_Q_NUM;
 module_param_cb(pf_q_num, &pf_q_num_ops, &pf_q_num, 0444);
 MODULE_PARM_DESC(pf_q_num, "Number of queues in PF(v1 1-4096, v2 1-1024)");
 
+static const struct kernel_param_ops vfs_num_ops = {
+       .set = vfs_num_set,
+       .get = param_get_int,
+};
+
 static u32 vfs_num;
-module_param(vfs_num, uint, 0444);
-MODULE_PARM_DESC(vfs_num, "Number of VFs to enable(1-63)");
+module_param_cb(vfs_num, &vfs_num_ops, &vfs_num, 0444);
+MODULE_PARM_DESC(vfs_num, "Number of VFs to enable(1-63), 0(default)");
 
 static const struct pci_device_id hisi_zip_dev_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_ZIP_PF) },
@@ -250,9 +241,9 @@ int zip_create_qps(struct hisi_qp **qps, int qp_num)
        return hisi_qm_alloc_qps_node(&zip_devices, qp_num, 0, node, qps);
 }
 
-static void hisi_zip_set_user_domain_and_cache(struct hisi_zip *hisi_zip)
+static int hisi_zip_set_user_domain_and_cache(struct hisi_qm *qm)
 {
-       void __iomem *base = hisi_zip->qm.io_base;
+       void __iomem *base = qm->io_base;
 
        /* qm user domain */
        writel(AXUSER_BASE, base + QM_ARUSER_M_CFG_1);
@@ -279,7 +270,7 @@ static void hisi_zip_set_user_domain_and_cache(struct hisi_zip *hisi_zip)
        writel(AXUSER_BASE, base + HZIP_SGL_RUSER_32_63);
        writel(AXUSER_BASE, base + HZIP_BD_WUSER_32_63);
 
-       if (hisi_zip->qm.use_sva) {
+       if (qm->use_sva) {
                writel(AXUSER_BASE | AXUSER_SSV, base + HZIP_DATA_RUSER_32_63);
                writel(AXUSER_BASE | AXUSER_SSV, base + HZIP_DATA_WUSER_32_63);
        } else {
@@ -295,10 +286,14 @@ static void hisi_zip_set_user_domain_and_cache(struct hisi_zip *hisi_zip)
        writel(SQC_CACHE_ENABLE | CQC_CACHE_ENABLE | SQC_CACHE_WB_ENABLE |
               CQC_CACHE_WB_ENABLE | FIELD_PREP(SQC_CACHE_WB_THRD, 1) |
               FIELD_PREP(CQC_CACHE_WB_THRD, 1), base + QM_CACHE_CTL);
+
+       return 0;
 }
 
 static void hisi_zip_hw_error_enable(struct hisi_qm *qm)
 {
+       u32 val;
+
        if (qm->ver == QM_HW_V1) {
                writel(HZIP_CORE_INT_MASK_ALL,
                       qm->io_base + HZIP_CORE_INT_MASK_REG);
@@ -317,12 +312,24 @@ static void hisi_zip_hw_error_enable(struct hisi_qm *qm)
 
        /* enable ZIP hw error interrupts */
        writel(0, qm->io_base + HZIP_CORE_INT_MASK_REG);
+
+       /* enable ZIP block master OOO when m-bit error occur */
+       val = readl(qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
+       val = val | HZIP_AXI_SHUTDOWN_ENABLE;
+       writel(val, qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
 }
 
 static void hisi_zip_hw_error_disable(struct hisi_qm *qm)
 {
+       u32 val;
+
        /* disable ZIP hw error interrupts */
        writel(HZIP_CORE_INT_MASK_ALL, qm->io_base + HZIP_CORE_INT_MASK_REG);
+
+       /* disable ZIP block master OOO when m-bit error occur */
+       val = readl(qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
+       val = val & ~HZIP_AXI_SHUTDOWN_ENABLE;
+       writel(val, qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
 }
 
 static inline struct hisi_qm *file_to_qm(struct ctrl_debug_file *file)
@@ -342,21 +349,20 @@ static u32 current_qm_read(struct ctrl_debug_file *file)
 static int current_qm_write(struct ctrl_debug_file *file, u32 val)
 {
        struct hisi_qm *qm = file_to_qm(file);
-       struct hisi_zip_ctrl *ctrl = file->ctrl;
        u32 vfq_num;
        u32 tmp;
 
-       if (val > ctrl->num_vfs)
+       if (val > qm->vfs_num)
                return -EINVAL;
 
        /* Calculate curr_qm_qp_num and store */
        if (val == 0) {
                qm->debug.curr_qm_qp_num = qm->qp_num;
        } else {
-               vfq_num = (qm->ctrl_qp_num - qm->qp_num) / ctrl->num_vfs;
-               if (val == ctrl->num_vfs)
+               vfq_num = (qm->ctrl_qp_num - qm->qp_num) / qm->vfs_num;
+               if (val == qm->vfs_num)
                        qm->debug.curr_qm_qp_num = qm->ctrl_qp_num -
-                               qm->qp_num - (ctrl->num_vfs - 1) * vfq_num;
+                               qm->qp_num - (qm->vfs_num - 1) * vfq_num;
                else
                        qm->debug.curr_qm_qp_num = vfq_num;
        }
@@ -477,6 +483,27 @@ static const struct file_operations ctrl_debug_fops = {
        .write = ctrl_debug_write,
 };
 
+
+static int zip_debugfs_atomic64_set(void *data, u64 val)
+{
+       if (val)
+               return -EINVAL;
+
+       atomic64_set((atomic64_t *)data, 0);
+
+       return 0;
+}
+
+static int zip_debugfs_atomic64_get(void *data, u64 *val)
+{
+       *val = atomic64_read((atomic64_t *)data);
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(zip_atomic64_ops, zip_debugfs_atomic64_get,
+                        zip_debugfs_atomic64_set, "%llu\n");
+
 static int hisi_zip_core_debug_init(struct hisi_zip_ctrl *ctrl)
 {
        struct hisi_zip *hisi_zip = ctrl->hisi_zip;
@@ -508,6 +535,25 @@ static int hisi_zip_core_debug_init(struct hisi_zip_ctrl *ctrl)
        return 0;
 }
 
+static void hisi_zip_dfx_debug_init(struct hisi_qm *qm)
+{
+       struct hisi_zip *zip = container_of(qm, struct hisi_zip, qm);
+       struct hisi_zip_dfx *dfx = &zip->dfx;
+       struct dentry *tmp_dir;
+       void *data;
+       int i;
+
+       tmp_dir = debugfs_create_dir("zip_dfx", qm->debug.debug_root);
+       for (i = 0; i < ARRAY_SIZE(zip_dfx_files); i++) {
+               data = (atomic64_t *)((uintptr_t)dfx + zip_dfx_files[i].offset);
+               debugfs_create_file(zip_dfx_files[i].name,
+                       0644,
+                       tmp_dir,
+                       data,
+                       &zip_atomic64_ops);
+       }
+}
+
 static int hisi_zip_ctrl_debug_init(struct hisi_zip_ctrl *ctrl)
 {
        int i;
@@ -534,6 +580,8 @@ static int hisi_zip_debugfs_init(struct hisi_zip *hisi_zip)
 
        dev_d = debugfs_create_dir(dev_name(dev), hzip_debugfs_root);
 
+       qm->debug.sqe_mask_offset = HZIP_SQE_MASK_OFFSET;
+       qm->debug.sqe_mask_len = HZIP_SQE_MASK_LEN;
        qm->debug.debug_root = dev_d;
        ret = hisi_qm_debug_init(qm);
        if (ret)
@@ -546,6 +594,8 @@ static int hisi_zip_debugfs_init(struct hisi_zip *hisi_zip)
                        goto failed_to_create;
        }
 
+       hisi_zip_dfx_debug_init(qm);
+
        return 0;
 
 failed_to_create:
@@ -598,8 +648,6 @@ static void hisi_zip_log_hw_error(struct hisi_qm *qm, u32 err_sts)
                }
                err++;
        }
-
-       writel(err_sts, qm->io_base + HZIP_CORE_INT_SOURCE);
 }
 
 static u32 hisi_zip_get_hw_err_status(struct hisi_qm *qm)
@@ -607,17 +655,55 @@ static u32 hisi_zip_get_hw_err_status(struct hisi_qm *qm)
        return readl(qm->io_base + HZIP_CORE_INT_STATUS);
 }
 
+static void hisi_zip_clear_hw_err_status(struct hisi_qm *qm, u32 err_sts)
+{
+       writel(err_sts, qm->io_base + HZIP_CORE_INT_SOURCE);
+}
+
+static void hisi_zip_open_axi_master_ooo(struct hisi_qm *qm)
+{
+       u32 val;
+
+       val = readl(qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
+
+       writel(val & ~HZIP_AXI_SHUTDOWN_ENABLE,
+              qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
+
+       writel(val | HZIP_AXI_SHUTDOWN_ENABLE,
+              qm->io_base + HZIP_SOFT_CTRL_ZIP_CONTROL);
+}
+
+static void hisi_zip_close_axi_master_ooo(struct hisi_qm *qm)
+{
+       u32 nfe_enb;
+
+       /* Disable ECC Mbit error report. */
+       nfe_enb = readl(qm->io_base + HZIP_CORE_INT_RAS_NFE_ENB);
+       writel(nfe_enb & ~HZIP_CORE_INT_STATUS_M_ECC,
+              qm->io_base + HZIP_CORE_INT_RAS_NFE_ENB);
+
+       /* Inject zip ECC Mbit error to block master ooo. */
+       writel(HZIP_CORE_INT_STATUS_M_ECC,
+              qm->io_base + HZIP_CORE_INT_SET);
+}
+
 static const struct hisi_qm_err_ini hisi_zip_err_ini = {
+       .hw_init                = hisi_zip_set_user_domain_and_cache,
        .hw_err_enable          = hisi_zip_hw_error_enable,
        .hw_err_disable         = hisi_zip_hw_error_disable,
        .get_dev_hw_err_status  = hisi_zip_get_hw_err_status,
+       .clear_dev_hw_err_status = hisi_zip_clear_hw_err_status,
        .log_dev_hw_err         = hisi_zip_log_hw_error,
+       .open_axi_master_ooo    = hisi_zip_open_axi_master_ooo,
+       .close_axi_master_ooo   = hisi_zip_close_axi_master_ooo,
        .err_info               = {
                .ce                     = QM_BASE_CE,
                .nfe                    = QM_BASE_NFE |
                                          QM_ACC_WB_NOT_READY_TIMEOUT,
                .fe                     = 0,
-               .msi                    = QM_DB_RANDOM_INVALID,
+               .ecc_2bits_mask         = HZIP_CORE_INT_STATUS_M_ECC,
+               .msi_wr_port            = HZIP_WR_PORT,
+               .acpi_rst               = "ZRST",
        }
 };
 
@@ -633,177 +719,85 @@ static int hisi_zip_pf_probe_init(struct hisi_zip *hisi_zip)
        hisi_zip->ctrl = ctrl;
        ctrl->hisi_zip = hisi_zip;
 
-       switch (qm->ver) {
-       case QM_HW_V1:
+       if (qm->ver == QM_HW_V1)
                qm->ctrl_qp_num = HZIP_QUEUE_NUM_V1;
-               break;
-
-       case QM_HW_V2:
+       else
                qm->ctrl_qp_num = HZIP_QUEUE_NUM_V2;
-               break;
-
-       default:
-               return -EINVAL;
-       }
 
        qm->err_ini = &hisi_zip_err_ini;
 
-       hisi_zip_set_user_domain_and_cache(hisi_zip);
+       hisi_zip_set_user_domain_and_cache(qm);
        hisi_qm_dev_err_init(qm);
        hisi_zip_debug_regs_clear(hisi_zip);
 
        return 0;
 }
 
-/* Currently we only support equal assignment */
-static int hisi_zip_vf_q_assign(struct hisi_zip *hisi_zip, int num_vfs)
+static int hisi_zip_qm_init(struct hisi_qm *qm, struct pci_dev *pdev)
 {
-       struct hisi_qm *qm = &hisi_zip->qm;
-       u32 qp_num = qm->qp_num;
-       u32 q_base = qp_num;
-       u32 q_num, remain_q_num, i;
-       int ret;
-
-       if (!num_vfs)
-               return -EINVAL;
-
-       remain_q_num = qm->ctrl_qp_num - qp_num;
-       if (remain_q_num < num_vfs)
-               return -EINVAL;
+       qm->pdev = pdev;
+       qm->ver = pdev->revision;
+       qm->algs = "zlib\ngzip";
+       qm->sqe_size = HZIP_SQE_SIZE;
+       qm->dev_name = hisi_zip_name;
 
-       q_num = remain_q_num / num_vfs;
-       for (i = 1; i <= num_vfs; i++) {
-               if (i == num_vfs)
-                       q_num += remain_q_num % num_vfs;
-               ret = hisi_qm_set_vft(qm, i, q_base, q_num);
-               if (ret)
-                       return ret;
-               q_base += q_num;
+       qm->fun_type = (pdev->device == PCI_DEVICE_ID_ZIP_PF) ?
+                       QM_HW_PF : QM_HW_VF;
+       if (qm->fun_type == QM_HW_PF) {
+               qm->qp_base = HZIP_PF_DEF_Q_BASE;
+               qm->qp_num = pf_q_num;
+               qm->qm_list = &zip_devices;
+       } else if (qm->fun_type == QM_HW_VF && qm->ver == QM_HW_V1) {
+               /*
+                * have no way to get qm configure in VM in v1 hardware,
+                * so currently force PF to uses HZIP_PF_DEF_Q_NUM, and force
+                * to trigger only one VF in v1 hardware.
+                *
+                * v2 hardware has no such problem.
+                */
+               qm->qp_base = HZIP_PF_DEF_Q_NUM;
+               qm->qp_num = HZIP_QUEUE_NUM_V1 - HZIP_PF_DEF_Q_NUM;
        }
 
-       return 0;
+       return hisi_qm_init(qm);
 }
 
-static int hisi_zip_clear_vft_config(struct hisi_zip *hisi_zip)
+static int hisi_zip_probe_init(struct hisi_zip *hisi_zip)
 {
-       struct hisi_zip_ctrl *ctrl = hisi_zip->ctrl;
        struct hisi_qm *qm = &hisi_zip->qm;
-       u32 i, num_vfs = ctrl->num_vfs;
        int ret;
 
-       for (i = 1; i <= num_vfs; i++) {
-               ret = hisi_qm_set_vft(qm, i, 0, 0);
+       if (qm->fun_type == QM_HW_PF) {
+               ret = hisi_zip_pf_probe_init(hisi_zip);
                if (ret)
                        return ret;
        }
 
-       ctrl->num_vfs = 0;
-
        return 0;
 }
 
-static int hisi_zip_sriov_enable(struct pci_dev *pdev, int max_vfs)
-{
-       struct hisi_zip *hisi_zip = pci_get_drvdata(pdev);
-       int pre_existing_vfs, num_vfs, ret;
-
-       pre_existing_vfs = pci_num_vf(pdev);
-
-       if (pre_existing_vfs) {
-               dev_err(&pdev->dev,
-                       "Can't enable VF. Please disable pre-enabled VFs!\n");
-               return 0;
-       }
-
-       num_vfs = min_t(int, max_vfs, HZIP_VF_NUM);
-
-       ret = hisi_zip_vf_q_assign(hisi_zip, num_vfs);
-       if (ret) {
-               dev_err(&pdev->dev, "Can't assign queues for VF!\n");
-               return ret;
-       }
-
-       hisi_zip->ctrl->num_vfs = num_vfs;
-
-       ret = pci_enable_sriov(pdev, num_vfs);
-       if (ret) {
-               dev_err(&pdev->dev, "Can't enable VF!\n");
-               hisi_zip_clear_vft_config(hisi_zip);
-               return ret;
-       }
-
-       return num_vfs;
-}
-
-static int hisi_zip_sriov_disable(struct pci_dev *pdev)
-{
-       struct hisi_zip *hisi_zip = pci_get_drvdata(pdev);
-
-       if (pci_vfs_assigned(pdev)) {
-               dev_err(&pdev->dev,
-                       "Can't disable VFs while VFs are assigned!\n");
-               return -EPERM;
-       }
-
-       /* remove in hisi_zip_pci_driver will be called to free VF resources */
-       pci_disable_sriov(pdev);
-
-       return hisi_zip_clear_vft_config(hisi_zip);
-}
-
 static int hisi_zip_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct hisi_zip *hisi_zip;
-       enum qm_hw_ver rev_id;
        struct hisi_qm *qm;
        int ret;
 
-       rev_id = hisi_qm_get_hw_version(pdev);
-       if (rev_id == QM_HW_UNKNOWN)
-               return -EINVAL;
-
        hisi_zip = devm_kzalloc(&pdev->dev, sizeof(*hisi_zip), GFP_KERNEL);
        if (!hisi_zip)
                return -ENOMEM;
-       pci_set_drvdata(pdev, hisi_zip);
 
        qm = &hisi_zip->qm;
-       qm->use_dma_api = true;
-       qm->pdev = pdev;
-       qm->ver = rev_id;
 
-       qm->algs = "zlib\ngzip";
-       qm->sqe_size = HZIP_SQE_SIZE;
-       qm->dev_name = hisi_zip_name;
-       qm->fun_type = (pdev->device == PCI_DEVICE_ID_ZIP_PF) ? QM_HW_PF :
-                                                               QM_HW_VF;
-       ret = hisi_qm_init(qm);
+       ret = hisi_zip_qm_init(qm, pdev);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to init qm!\n");
+               pci_err(pdev, "Failed to init ZIP QM (%d)!\n", ret);
                return ret;
        }
 
-       if (qm->fun_type == QM_HW_PF) {
-               ret = hisi_zip_pf_probe_init(hisi_zip);
-               if (ret)
-                       return ret;
-
-               qm->qp_base = HZIP_PF_DEF_Q_BASE;
-               qm->qp_num = pf_q_num;
-       } else if (qm->fun_type == QM_HW_VF) {
-               /*
-                * have no way to get qm configure in VM in v1 hardware,
-                * so currently force PF to uses HZIP_PF_DEF_Q_NUM, and force
-                * to trigger only one VF in v1 hardware.
-                *
-                * v2 hardware has no such problem.
-                */
-               if (qm->ver == QM_HW_V1) {
-                       qm->qp_base = HZIP_PF_DEF_Q_NUM;
-                       qm->qp_num = HZIP_QUEUE_NUM_V1 - HZIP_PF_DEF_Q_NUM;
-               } else if (qm->ver == QM_HW_V2)
-                       /* v2 starts to support get vft by mailbox */
-                       hisi_qm_get_vft(qm, &qm->qp_base, &qm->qp_num);
+       ret = hisi_zip_probe_init(hisi_zip);
+       if (ret) {
+               pci_err(pdev, "Failed to probe (%d)!\n", ret);
+               goto err_qm_uninit;
        }
 
        ret = hisi_qm_start(qm);
@@ -823,7 +817,7 @@ static int hisi_zip_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 
        if (qm->fun_type == QM_HW_PF && vfs_num > 0) {
-               ret = hisi_zip_sriov_enable(pdev, vfs_num);
+               ret = hisi_qm_sriov_enable(pdev, vfs_num);
                if (ret < 0)
                        goto err_remove_from_list;
        }
@@ -836,15 +830,8 @@ err_remove_from_list:
        hisi_qm_stop(qm);
 err_qm_uninit:
        hisi_qm_uninit(qm);
-       return ret;
-}
 
-static int hisi_zip_sriov_configure(struct pci_dev *pdev, int num_vfs)
-{
-       if (num_vfs == 0)
-               return hisi_zip_sriov_disable(pdev);
-       else
-               return hisi_zip_sriov_enable(pdev, num_vfs);
+       return ret;
 }
 
 static void hisi_zip_remove(struct pci_dev *pdev)
@@ -852,8 +839,8 @@ static void hisi_zip_remove(struct pci_dev *pdev)
        struct hisi_zip *hisi_zip = pci_get_drvdata(pdev);
        struct hisi_qm *qm = &hisi_zip->qm;
 
-       if (qm->fun_type == QM_HW_PF && hisi_zip->ctrl->num_vfs != 0)
-               hisi_zip_sriov_disable(pdev);
+       if (qm->fun_type == QM_HW_PF && qm->vfs_num)
+               hisi_qm_sriov_disable(pdev);
 
        hisi_zip_debugfs_exit(hisi_zip);
        hisi_qm_stop(qm);
@@ -865,6 +852,9 @@ static void hisi_zip_remove(struct pci_dev *pdev)
 
 static const struct pci_error_handlers hisi_zip_err_handler = {
        .error_detected = hisi_qm_dev_err_detected,
+       .slot_reset     = hisi_qm_dev_slot_reset,
+       .reset_prepare  = hisi_qm_reset_prepare,
+       .reset_done     = hisi_qm_reset_done,
 };
 
 static struct pci_driver hisi_zip_pci_driver = {
@@ -873,7 +863,7 @@ static struct pci_driver hisi_zip_pci_driver = {
        .probe                  = hisi_zip_probe,
        .remove                 = hisi_zip_remove,
        .sriov_configure        = IS_ENABLED(CONFIG_PCI_IOV) ?
-                                       hisi_zip_sriov_configure : NULL,
+                                       hisi_qm_sriov_configure : NULL,
        .err_handler            = &hisi_zip_err_handler,
 };
 
index 200fb3303db0eecc690d703a8ebdcbd4ea1ea1b7..34bb3063eb707947af3a0247f19476a02dccc38d 100644 (file)
@@ -79,13 +79,13 @@ static int otx_cpt_device_init(struct otx_cpt_device *cpt)
        /* Check BIST status */
        bist = (u64)otx_cpt_check_bist_status(cpt);
        if (bist) {
-               dev_err(dev, "RAM BIST failed with code 0x%llx", bist);
+               dev_err(dev, "RAM BIST failed with code 0x%llx\n", bist);
                return -ENODEV;
        }
 
        bist = otx_cpt_check_exe_bist_status(cpt);
        if (bist) {
-               dev_err(dev, "Engine BIST failed with code 0x%llx", bist);
+               dev_err(dev, "Engine BIST failed with code 0x%llx\n", bist);
                return -ENODEV;
        }
 
index a6774232e9a3c88968217307d7cf17cbed58a21d..a9e3de65875ac3f351770a45a789d7d315acd4ee 100644 (file)
@@ -63,11 +63,11 @@ static void dump_mbox_msg(struct otx_cpt_mbox *mbox_msg, int vf_id)
        hex_dump_to_buffer(mbox_msg, sizeof(struct otx_cpt_mbox), 16, 8,
                           raw_data_str, OTX_CPT_MAX_MBOX_DATA_STR_SIZE, false);
        if (vf_id >= 0)
-               pr_debug("MBOX opcode %s received from VF%d raw_data %s",
+               pr_debug("MBOX opcode %s received from VF%d raw_data %s\n",
                         get_mbox_opcode_str(mbox_msg->msg), vf_id,
                         raw_data_str);
        else
-               pr_debug("MBOX opcode %s received from PF raw_data %s",
+               pr_debug("MBOX opcode %s received from PF raw_data %s\n",
                         get_mbox_opcode_str(mbox_msg->msg), raw_data_str);
 }
 
@@ -140,20 +140,20 @@ static int otx_cpt_bind_vq_to_grp(struct otx_cpt_device *cpt, u8 q, u8 grp)
        struct otx_cpt_ucode *ucode;
 
        if (q >= cpt->max_vfs) {
-               dev_err(dev, "Requested queue %d is > than maximum avail %d",
+               dev_err(dev, "Requested queue %d is > than maximum avail %d\n",
                        q, cpt->max_vfs);
                return -EINVAL;
        }
 
        if (grp >= OTX_CPT_MAX_ENGINE_GROUPS) {
-               dev_err(dev, "Requested group %d is > than maximum avail %d",
+               dev_err(dev, "Requested group %d is > than maximum avail %d\n",
                        grp, OTX_CPT_MAX_ENGINE_GROUPS);
                return -EINVAL;
        }
 
        eng_grp = &cpt->eng_grps.grp[grp];
        if (!eng_grp->is_enabled) {
-               dev_err(dev, "Requested engine group %d is disabled", grp);
+               dev_err(dev, "Requested engine group %d is disabled\n", grp);
                return -EINVAL;
        }
 
@@ -212,7 +212,7 @@ static void otx_cpt_handle_mbox_intr(struct otx_cpt_device *cpt, int vf)
                vftype = otx_cpt_bind_vq_to_grp(cpt, vf, (u8)mbx.data);
                if ((vftype != OTX_CPT_AE_TYPES) &&
                    (vftype != OTX_CPT_SE_TYPES)) {
-                       dev_err(dev, "VF%d binding to eng group %llu failed",
+                       dev_err(dev, "VF%d binding to eng group %llu failed\n",
                                vf, mbx.data);
                        otx_cptpf_mbox_send_nack(cpt, vf, &mbx);
                } else {
index d04baa319592540329dcce047b47691d42df1f25..fec8f3b9b1128dfccfa3e6cd9a80baee4325c73b 100644 (file)
@@ -62,7 +62,7 @@ static struct otx_cpt_bitmap get_cores_bmap(struct device *dev,
        int i;
 
        if (eng_grp->g->engs_num > OTX_CPT_MAX_ENGINES) {
-               dev_err(dev, "unsupported number of engines %d on octeontx",
+               dev_err(dev, "unsupported number of engines %d on octeontx\n",
                        eng_grp->g->engs_num);
                return bmap;
        }
@@ -78,7 +78,7 @@ static struct otx_cpt_bitmap get_cores_bmap(struct device *dev,
        }
 
        if (!found)
-               dev_err(dev, "No engines reserved for engine group %d",
+               dev_err(dev, "No engines reserved for engine group %d\n",
                        eng_grp->idx);
        return bmap;
 }
@@ -306,7 +306,7 @@ static int process_tar_file(struct device *dev,
        ucode_size = ntohl(ucode_hdr->code_length) * 2;
        if (!ucode_size || (size < round_up(ucode_size, 16) +
            sizeof(struct otx_cpt_ucode_hdr) + OTX_CPT_UCODE_SIGN_LEN)) {
-               dev_err(dev, "Ucode %s invalid size", filename);
+               dev_err(dev, "Ucode %s invalid size\n", filename);
                return -EINVAL;
        }
 
@@ -379,18 +379,18 @@ static void print_tar_dbg_info(struct tar_arch_info_t *tar_arch,
 {
        struct tar_ucode_info_t *curr;
 
-       pr_debug("Tar archive filename %s", tar_filename);
-       pr_debug("Tar archive pointer %p, size %ld", tar_arch->fw->data,
+       pr_debug("Tar archive filename %s\n", tar_filename);
+       pr_debug("Tar archive pointer %p, size %ld\n", tar_arch->fw->data,
                 tar_arch->fw->size);
        list_for_each_entry(curr, &tar_arch->ucodes, list) {
-               pr_debug("Ucode filename %s", curr->ucode.filename);
-               pr_debug("Ucode version string %s", curr->ucode.ver_str);
-               pr_debug("Ucode version %d.%d.%d.%d",
+               pr_debug("Ucode filename %s\n", curr->ucode.filename);
+               pr_debug("Ucode version string %s\n", curr->ucode.ver_str);
+               pr_debug("Ucode version %d.%d.%d.%d\n",
                         curr->ucode.ver_num.nn, curr->ucode.ver_num.xx,
                         curr->ucode.ver_num.yy, curr->ucode.ver_num.zz);
-               pr_debug("Ucode type (%d) %s", curr->ucode.type,
+               pr_debug("Ucode type (%d) %s\n", curr->ucode.type,
                         get_ucode_type_str(curr->ucode.type));
-               pr_debug("Ucode size %d", curr->ucode.size);
+               pr_debug("Ucode size %d\n", curr->ucode.size);
                pr_debug("Ucode ptr %p\n", curr->ucode_ptr);
        }
 }
@@ -417,14 +417,14 @@ static struct tar_arch_info_t *load_tar_archive(struct device *dev,
                goto release_tar_arch;
 
        if (tar_arch->fw->size < TAR_BLOCK_LEN) {
-               dev_err(dev, "Invalid tar archive %s ", tar_filename);
+               dev_err(dev, "Invalid tar archive %s\n", tar_filename);
                goto release_tar_arch;
        }
 
        tar_size = tar_arch->fw->size;
        tar_blk = (struct tar_blk_t *) tar_arch->fw->data;
        if (strncmp(tar_blk->hdr.magic, TAR_MAGIC, TAR_MAGIC_LEN - 1)) {
-               dev_err(dev, "Unsupported format of tar archive %s",
+               dev_err(dev, "Unsupported format of tar archive %s\n",
                        tar_filename);
                goto release_tar_arch;
        }
@@ -437,7 +437,7 @@ static struct tar_arch_info_t *load_tar_archive(struct device *dev,
 
                if (tar_offs + cur_size > tar_size ||
                    tar_offs + 2*TAR_BLOCK_LEN > tar_size) {
-                       dev_err(dev, "Invalid tar archive %s ", tar_filename);
+                       dev_err(dev, "Invalid tar archive %s\n", tar_filename);
                        goto release_tar_arch;
                }
 
@@ -458,7 +458,7 @@ static struct tar_arch_info_t *load_tar_archive(struct device *dev,
 
                /* Check for the end of the archive */
                if (tar_offs + 2*TAR_BLOCK_LEN > tar_size) {
-                       dev_err(dev, "Invalid tar archive %s ", tar_filename);
+                       dev_err(dev, "Invalid tar archive %s\n", tar_filename);
                        goto release_tar_arch;
                }
 
@@ -563,13 +563,13 @@ static void print_engs_info(struct otx_cpt_eng_grp_info *eng_grp,
 
 static void print_ucode_dbg_info(struct otx_cpt_ucode *ucode)
 {
-       pr_debug("Ucode info");
-       pr_debug("Ucode version string %s", ucode->ver_str);
-       pr_debug("Ucode version %d.%d.%d.%d", ucode->ver_num.nn,
+       pr_debug("Ucode info\n");
+       pr_debug("Ucode version string %s\n", ucode->ver_str);
+       pr_debug("Ucode version %d.%d.%d.%d\n", ucode->ver_num.nn,
                 ucode->ver_num.xx, ucode->ver_num.yy, ucode->ver_num.zz);
-       pr_debug("Ucode type %s", get_ucode_type_str(ucode->type));
-       pr_debug("Ucode size %d", ucode->size);
-       pr_debug("Ucode virt address %16.16llx", (u64)ucode->align_va);
+       pr_debug("Ucode type %s\n", get_ucode_type_str(ucode->type));
+       pr_debug("Ucode size %d\n", ucode->size);
+       pr_debug("Ucode virt address %16.16llx\n", (u64)ucode->align_va);
        pr_debug("Ucode phys address %16.16llx\n", ucode->align_dma);
 }
 
@@ -600,19 +600,19 @@ static void print_dbg_info(struct device *dev,
        u32 mask[4];
        int i, j;
 
-       pr_debug("Engine groups global info");
-       pr_debug("max SE %d, max AE %d",
+       pr_debug("Engine groups global info\n");
+       pr_debug("max SE %d, max AE %d\n",
                 eng_grps->avail.max_se_cnt, eng_grps->avail.max_ae_cnt);
-       pr_debug("free SE %d", eng_grps->avail.se_cnt);
-       pr_debug("free AE %d", eng_grps->avail.ae_cnt);
+       pr_debug("free SE %d\n", eng_grps->avail.se_cnt);
+       pr_debug("free AE %d\n", eng_grps->avail.ae_cnt);
 
        for (i = 0; i < OTX_CPT_MAX_ENGINE_GROUPS; i++) {
                grp = &eng_grps->grp[i];
-               pr_debug("engine_group%d, state %s", i, grp->is_enabled ?
+               pr_debug("engine_group%d, state %s\n", i, grp->is_enabled ?
                         "enabled" : "disabled");
                if (grp->is_enabled) {
                        mirrored_grp = &eng_grps->grp[grp->mirror.idx];
-                       pr_debug("Ucode0 filename %s, version %s",
+                       pr_debug("Ucode0 filename %s, version %s\n",
                                 grp->mirror.is_ena ?
                                 mirrored_grp->ucode[0].filename :
                                 grp->ucode[0].filename,
@@ -626,18 +626,18 @@ static void print_dbg_info(struct device *dev,
                        if (engs->type) {
                                print_engs_info(grp, engs_info,
                                                2*OTX_CPT_UCODE_NAME_LENGTH, j);
-                               pr_debug("Slot%d: %s", j, engs_info);
+                               pr_debug("Slot%d: %s\n", j, engs_info);
                                bitmap_to_arr32(mask, engs->bmap,
                                                eng_grps->engs_num);
-                               pr_debug("Mask:  %8.8x %8.8x %8.8x %8.8x",
+                               pr_debug("Mask: %8.8x %8.8x %8.8x %8.8x\n",
                                         mask[3], mask[2], mask[1], mask[0]);
                        } else
-                               pr_debug("Slot%d not used", j);
+                               pr_debug("Slot%d not used\n", j);
                }
                if (grp->is_enabled) {
                        cpt_print_engines_mask(grp, dev, engs_mask,
                                               OTX_CPT_UCODE_NAME_LENGTH);
-                       pr_debug("Cmask: %s", engs_mask);
+                       pr_debug("Cmask: %s\n", engs_mask);
                }
        }
 }
@@ -766,7 +766,7 @@ static int check_engines_availability(struct device *dev,
 
        if (avail_cnt < req_eng->count) {
                dev_err(dev,
-                       "Error available %s engines %d < than requested %d",
+                       "Error available %s engines %d < than requested %d\n",
                        get_eng_type_str(req_eng->type),
                        avail_cnt, req_eng->count);
                return -EBUSY;
@@ -867,7 +867,7 @@ static int copy_ucode_to_dma_mem(struct device *dev,
                                       OTX_CPT_UCODE_ALIGNMENT,
                                       &ucode->dma, GFP_KERNEL);
        if (!ucode->va) {
-               dev_err(dev, "Unable to allocate space for microcode");
+               dev_err(dev, "Unable to allocate space for microcode\n");
                return -ENOMEM;
        }
        ucode->align_va = PTR_ALIGN(ucode->va, OTX_CPT_UCODE_ALIGNMENT);
@@ -905,15 +905,15 @@ static int ucode_load(struct device *dev, struct otx_cpt_ucode *ucode,
        ucode->size = ntohl(ucode_hdr->code_length) * 2;
        if (!ucode->size || (fw->size < round_up(ucode->size, 16)
            + sizeof(struct otx_cpt_ucode_hdr) + OTX_CPT_UCODE_SIGN_LEN)) {
-               dev_err(dev, "Ucode %s invalid size", ucode_filename);
+               dev_err(dev, "Ucode %s invalid size\n", ucode_filename);
                ret = -EINVAL;
                goto release_fw;
        }
 
        ret = get_ucode_type(ucode_hdr, &ucode->type);
        if (ret) {
-               dev_err(dev, "Microcode %s unknown type 0x%x", ucode->filename,
-                       ucode->type);
+               dev_err(dev, "Microcode %s unknown type 0x%x\n",
+                       ucode->filename, ucode->type);
                goto release_fw;
        }
 
@@ -1083,7 +1083,7 @@ static int eng_grp_update_masks(struct device *dev,
                        break;
 
                default:
-                       dev_err(dev, "Invalid engine type %d", engs->type);
+                       dev_err(dev, "Invalid engine type %d\n", engs->type);
                        return -EINVAL;
                }
 
@@ -1142,13 +1142,14 @@ static int delete_engine_group(struct device *dev,
                return -EINVAL;
 
        if (eng_grp->mirror.ref_count) {
-               dev_err(dev, "Can't delete engine_group%d as it is used by:",
+               dev_err(dev, "Can't delete engine_group%d as it is used by engine_group(s):",
                        eng_grp->idx);
                for (i = 0; i < OTX_CPT_MAX_ENGINE_GROUPS; i++) {
                        if (eng_grp->g->grp[i].mirror.is_ena &&
                            eng_grp->g->grp[i].mirror.idx == eng_grp->idx)
-                               dev_err(dev, "engine_group%d", i);
+                               pr_cont(" %d", i);
                }
+               pr_cont("\n");
                return -EINVAL;
        }
 
@@ -1182,7 +1183,7 @@ static int validate_1_ucode_scenario(struct device *dev,
                if (!otx_cpt_uc_supports_eng_type(&eng_grp->ucode[0],
                                                  engs[i].type)) {
                        dev_err(dev,
-                               "Microcode %s does not support %s engines",
+                               "Microcode %s does not support %s engines\n",
                                eng_grp->ucode[0].filename,
                                get_eng_type_str(engs[i].type));
                        return -EINVAL;
@@ -1220,7 +1221,7 @@ static int create_engine_group(struct device *dev,
        /* Validate if requested engine types are supported by this device */
        for (i = 0; i < engs_cnt; i++)
                if (!dev_supports_eng_type(eng_grps, engs[i].type)) {
-                       dev_err(dev, "Device does not support %s engines",
+                       dev_err(dev, "Device does not support %s engines\n",
                                get_eng_type_str(engs[i].type));
                        return -EPERM;
                }
@@ -1228,7 +1229,7 @@ static int create_engine_group(struct device *dev,
        /* Find engine group which is not used */
        eng_grp = find_unused_eng_grp(eng_grps);
        if (!eng_grp) {
-               dev_err(dev, "Error all engine groups are being used");
+               dev_err(dev, "Error all engine groups are being used\n");
                return -ENOSPC;
        }
 
@@ -1298,11 +1299,11 @@ static int create_engine_group(struct device *dev,
        eng_grp->is_enabled = true;
        if (eng_grp->mirror.is_ena)
                dev_info(dev,
-                        "Engine_group%d: reuse microcode %s from group %d",
+                        "Engine_group%d: reuse microcode %s from group %d\n",
                         eng_grp->idx, mirrored_eng_grp->ucode[0].ver_str,
                         mirrored_eng_grp->idx);
        else
-               dev_info(dev, "Engine_group%d: microcode loaded %s",
+               dev_info(dev, "Engine_group%d: microcode loaded %s\n",
                         eng_grp->idx, eng_grp->ucode[0].ver_str);
 
        return 0;
@@ -1412,14 +1413,14 @@ static ssize_t ucode_load_store(struct device *dev,
        } else {
                if (del_grp_idx < 0 ||
                    del_grp_idx >= OTX_CPT_MAX_ENGINE_GROUPS) {
-                       dev_err(dev, "Invalid engine group index %d",
+                       dev_err(dev, "Invalid engine group index %d\n",
                                del_grp_idx);
                        ret = -EINVAL;
                        return ret;
                }
 
                if (!eng_grps->grp[del_grp_idx].is_enabled) {
-                       dev_err(dev, "Error engine_group%d is not configured",
+                       dev_err(dev, "Error engine_group%d is not configured\n",
                                del_grp_idx);
                        ret = -EINVAL;
                        return ret;
@@ -1568,7 +1569,7 @@ void otx_cpt_disable_all_cores(struct otx_cpt_device *cpt)
                udelay(CSR_DELAY);
                reg = readq(cpt->reg_base + OTX_CPT_PF_EXEC_BUSY);
                if (timeout--) {
-                       dev_warn(&cpt->pdev->dev, "Cores still busy");
+                       dev_warn(&cpt->pdev->dev, "Cores still busy\n");
                        break;
                }
        }
@@ -1626,7 +1627,7 @@ int otx_cpt_init_eng_grps(struct pci_dev *pdev,
                             eng_grps->avail.max_ae_cnt;
        if (eng_grps->engs_num > OTX_CPT_MAX_ENGINES) {
                dev_err(&pdev->dev,
-                       "Number of engines %d > than max supported %d",
+                       "Number of engines %d > than max supported %d\n",
                        eng_grps->engs_num, OTX_CPT_MAX_ENGINES);
                ret = -EINVAL;
                goto err;
index 06202bcffb33fe7cd91a5311588726c3c73b00c4..60e744f680d34713df9d99720f4d2c04c2b6216d 100644 (file)
@@ -1660,7 +1660,7 @@ int otx_cpt_crypto_init(struct pci_dev *pdev, struct module *mod,
        case OTX_CPT_SE_TYPES:
                count = atomic_read(&se_devices.count);
                if (count >= CPT_MAX_VF_NUM) {
-                       dev_err(&pdev->dev, "No space to add a new device");
+                       dev_err(&pdev->dev, "No space to add a new device\n");
                        ret = -ENOSPC;
                        goto err;
                }
@@ -1687,7 +1687,7 @@ int otx_cpt_crypto_init(struct pci_dev *pdev, struct module *mod,
        case OTX_CPT_AE_TYPES:
                count = atomic_read(&ae_devices.count);
                if (count >= CPT_MAX_VF_NUM) {
-                       dev_err(&pdev->dev, "No space to a add new device");
+                       dev_err(&pdev->dev, "No space to a add new device\n");
                        ret = -ENOSPC;
                        goto err;
                }
@@ -1728,7 +1728,7 @@ void otx_cpt_crypto_exit(struct pci_dev *pdev, struct module *mod,
                }
 
        if (!dev_found) {
-               dev_err(&pdev->dev, "%s device not found", __func__);
+               dev_err(&pdev->dev, "%s device not found\n", __func__);
                goto exit;
        }
 
index a91860b5dc77554f1305761d617a4fc1d117aa07..ce3168327a39c34401fb211570ad38007a4d8cdf 100644 (file)
@@ -584,7 +584,7 @@ static irqreturn_t cptvf_done_intr_handler(int __always_unused irq,
                cptvf_write_vq_done_ack(cptvf, intr);
                wqe = get_cptvf_vq_wqe(cptvf, 0);
                if (unlikely(!wqe)) {
-                       dev_err(&pdev->dev, "No work to schedule for VF (%d)",
+                       dev_err(&pdev->dev, "No work to schedule for VF (%d)\n",
                                cptvf->vfid);
                        return IRQ_NONE;
                }
@@ -602,7 +602,7 @@ static void cptvf_set_irq_affinity(struct otx_cptvf *cptvf, int vec)
        if (!zalloc_cpumask_var(&cptvf->affinity_mask[vec],
                                GFP_KERNEL)) {
                dev_err(&pdev->dev,
-                       "Allocation failed for affinity_mask for VF %d",
+                       "Allocation failed for affinity_mask for VF %d\n",
                        cptvf->vfid);
                return;
        }
@@ -691,7 +691,7 @@ static ssize_t vf_engine_group_store(struct device *dev,
                return -EINVAL;
 
        if (val >= OTX_CPT_MAX_ENGINE_GROUPS) {
-               dev_err(dev, "Engine group >= than max available groups %d",
+               dev_err(dev, "Engine group >= than max available groups %d\n",
                        OTX_CPT_MAX_ENGINE_GROUPS);
                return -EINVAL;
        }
@@ -837,7 +837,7 @@ static int otx_cptvf_probe(struct pci_dev *pdev,
                          cptvf_misc_intr_handler, 0, "CPT VF misc intr",
                          cptvf);
        if (err) {
-               dev_err(dev, "Failed to request misc irq");
+               dev_err(dev, "Failed to request misc irq\n");
                goto free_vectors;
        }
 
@@ -854,7 +854,7 @@ static int otx_cptvf_probe(struct pci_dev *pdev,
        cptvf->cqinfo.qchunksize = OTX_CPT_CMD_QCHUNK_SIZE;
        err = cptvf_sw_init(cptvf, OTX_CPT_CMD_QLEN, OTX_CPT_NUM_QS_PER_VF);
        if (err) {
-               dev_err(dev, "cptvf_sw_init() failed");
+               dev_err(dev, "cptvf_sw_init() failed\n");
                goto free_misc_irq;
        }
        /* Convey VQ LEN to PF */
@@ -946,7 +946,7 @@ static void otx_cptvf_remove(struct pci_dev *pdev)
 
        /* Convey DOWN to PF */
        if (otx_cptvf_send_vf_down(cptvf)) {
-               dev_err(&pdev->dev, "PF not responding to DOWN msg");
+               dev_err(&pdev->dev, "PF not responding to DOWN msg\n");
        } else {
                sysfs_remove_group(&pdev->dev.kobj, &otx_cptvf_sysfs_group);
                otx_cpt_crypto_exit(pdev, THIS_MODULE, cptvf->vftype);
index df839b8803547057478edabeea37f0fb6ad1c977..239195cccf93f4f83b5cea0bf584766d6e667ceb 100644 (file)
@@ -314,7 +314,7 @@ static int process_request(struct pci_dev *pdev, struct otx_cpt_req_info *req,
                                                              GFP_ATOMIC;
        ret = setup_sgio_list(pdev, &info, req, gfp);
        if (unlikely(ret)) {
-               dev_err(&pdev->dev, "Setting up SG list failed");
+               dev_err(&pdev->dev, "Setting up SG list failed\n");
                goto request_cleanup;
        }
        cpt_req->dlen = info->dlen;
@@ -410,17 +410,17 @@ int otx_cpt_do_request(struct pci_dev *pdev, struct otx_cpt_req_info *req,
        struct otx_cptvf *cptvf = pci_get_drvdata(pdev);
 
        if (!otx_cpt_device_ready(cptvf)) {
-               dev_err(&pdev->dev, "CPT Device is not ready");
+               dev_err(&pdev->dev, "CPT Device is not ready\n");
                return -ENODEV;
        }
 
        if ((cptvf->vftype == OTX_CPT_SE_TYPES) && (!req->ctrl.s.se_req)) {
-               dev_err(&pdev->dev, "CPTVF-%d of SE TYPE got AE request",
+               dev_err(&pdev->dev, "CPTVF-%d of SE TYPE got AE request\n",
                        cptvf->vfid);
                return -EINVAL;
        } else if ((cptvf->vftype == OTX_CPT_AE_TYPES) &&
                   (req->ctrl.s.se_req)) {
-               dev_err(&pdev->dev, "CPTVF-%d of AE TYPE got SE request",
+               dev_err(&pdev->dev, "CPTVF-%d of AE TYPE got SE request\n",
                        cptvf->vfid);
                return -EINVAL;
        }
@@ -461,7 +461,7 @@ static int cpt_process_ccode(struct pci_dev *pdev,
                /* check for timeout */
                if (time_after_eq(jiffies, cpt_info->time_in +
                                  OTX_CPT_COMMAND_TIMEOUT * HZ))
-                       dev_warn(&pdev->dev, "Request timed out 0x%p", req);
+                       dev_warn(&pdev->dev, "Request timed out 0x%p\n", req);
                else if (cpt_info->extra_time < OTX_CPT_TIME_IN_RESET_COUNT) {
                        cpt_info->time_in = jiffies;
                        cpt_info->extra_time++;
index bd6309e57ab81a3d4336f79aa92e9ba555bebf3c..da3f0b8814aa49cb7ee8f5c94a27908391975219 100644 (file)
@@ -805,12 +805,9 @@ static int mtk_sha_setkey(struct crypto_ahash *tfm, const u8 *key,
        size_t ds = crypto_shash_digestsize(bctx->shash);
        int err, i;
 
-       SHASH_DESC_ON_STACK(shash, bctx->shash);
-
-       shash->tfm = bctx->shash;
-
        if (keylen > bs) {
-               err = crypto_shash_digest(shash, key, keylen, bctx->ipad);
+               err = crypto_shash_tfm_digest(bctx->shash, key, keylen,
+                                             bctx->ipad);
                if (err)
                        return err;
                keylen = ds;
index f5c468f2cc82e95595556b3693fab86e02bd902b..6a828bbecea40027d1e50aeb5eefc659e5fa8c7b 100644 (file)
@@ -462,7 +462,6 @@ static int n2_hmac_async_setkey(struct crypto_ahash *tfm, const u8 *key,
        struct n2_hmac_ctx *ctx = crypto_ahash_ctx(tfm);
        struct crypto_shash *child_shash = ctx->child_shash;
        struct crypto_ahash *fallback_tfm;
-       SHASH_DESC_ON_STACK(shash, child_shash);
        int err, bs, ds;
 
        fallback_tfm = ctx->base.fallback_tfm;
@@ -470,14 +469,12 @@ static int n2_hmac_async_setkey(struct crypto_ahash *tfm, const u8 *key,
        if (err)
                return err;
 
-       shash->tfm = child_shash;
-
        bs = crypto_shash_blocksize(child_shash);
        ds = crypto_shash_digestsize(child_shash);
        BUG_ON(ds > N2_HASH_KEY_MAX);
        if (keylen > bs) {
-               err = crypto_shash_digest(shash, key, keylen,
-                                         ctx->hash_key);
+               err = crypto_shash_tfm_digest(child_shash, key, keylen,
+                                             ctx->hash_key);
                if (err)
                        return err;
                keylen = ds;
index e4072cd385857cffae2bf41ecdcc82282c079e73..063ad5d03f333cfa999455b841899c4170f49d6a 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/of_irq.h>
 #include <linux/delay.h>
 #include <linux/crypto.h>
-#include <linux/cryptohash.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/algapi.h>
 #include <crypto/sha.h>
@@ -1245,16 +1244,6 @@ static int omap_sham_update(struct ahash_request *req)
        return omap_sham_enqueue(req, OP_UPDATE);
 }
 
-static int omap_sham_shash_digest(struct crypto_shash *tfm, u32 flags,
-                                 const u8 *data, unsigned int len, u8 *out)
-{
-       SHASH_DESC_ON_STACK(shash, tfm);
-
-       shash->tfm = tfm;
-
-       return crypto_shash_digest(shash, data, len, out);
-}
-
 static int omap_sham_final_shash(struct ahash_request *req)
 {
        struct omap_sham_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
@@ -1270,9 +1259,8 @@ static int omap_sham_final_shash(struct ahash_request *req)
            !test_bit(FLAGS_AUTO_XOR, &ctx->dd->flags))
                offset = get_block_size(ctx);
 
-       return omap_sham_shash_digest(tctx->fallback, req->base.flags,
-                                     ctx->buffer + offset,
-                                     ctx->bufcnt - offset, req->result);
+       return crypto_shash_tfm_digest(tctx->fallback, ctx->buffer + offset,
+                                      ctx->bufcnt - offset, req->result);
 }
 
 static int omap_sham_final(struct ahash_request *req)
@@ -1351,9 +1339,8 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key,
                return err;
 
        if (keylen > bs) {
-               err = omap_sham_shash_digest(bctx->shash,
-                               crypto_shash_get_flags(bctx->shash),
-                               key, keylen, bctx->ipad);
+               err = crypto_shash_tfm_digest(bctx->shash, key, keylen,
+                                             bctx->ipad);
                if (err)
                        return err;
                keylen = ds;
index 2a16800d257954a0783f116a21882f1800079f1c..341433fbcc4a8bdffdfb6ff1ae5340a8c35f88fd 100644 (file)
@@ -1520,37 +1520,6 @@ static int s5p_hash_update(struct ahash_request *req)
        return s5p_hash_enqueue(req, true); /* HASH_OP_UPDATE */
 }
 
-/**
- * s5p_hash_shash_digest() - calculate shash digest
- * @tfm:       crypto transformation
- * @flags:     tfm flags
- * @data:      input data
- * @len:       length of data
- * @out:       output buffer
- */
-static int s5p_hash_shash_digest(struct crypto_shash *tfm, u32 flags,
-                                const u8 *data, unsigned int len, u8 *out)
-{
-       SHASH_DESC_ON_STACK(shash, tfm);
-
-       shash->tfm = tfm;
-
-       return crypto_shash_digest(shash, data, len, out);
-}
-
-/**
- * s5p_hash_final_shash() - calculate shash digest
- * @req:       AHASH request
- */
-static int s5p_hash_final_shash(struct ahash_request *req)
-{
-       struct s5p_hash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
-       struct s5p_hash_reqctx *ctx = ahash_request_ctx(req);
-
-       return s5p_hash_shash_digest(tctx->fallback, req->base.flags,
-                                    ctx->buffer, ctx->bufcnt, req->result);
-}
-
 /**
  * s5p_hash_final() - close up hash and calculate digest
  * @req:       AHASH request
@@ -1582,8 +1551,12 @@ static int s5p_hash_final(struct ahash_request *req)
        if (ctx->error)
                return -EINVAL; /* uncompleted hash is not needed */
 
-       if (!ctx->digcnt && ctx->bufcnt < BUFLEN)
-               return s5p_hash_final_shash(req);
+       if (!ctx->digcnt && ctx->bufcnt < BUFLEN) {
+               struct s5p_hash_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+
+               return crypto_shash_tfm_digest(tctx->fallback, ctx->buffer,
+                                              ctx->bufcnt, req->result);
+       }
 
        return s5p_hash_enqueue(req, false); /* HASH_OP_FINAL */
 }
index 8e92e4ac79f11fbb94352ffc7d510c26af1af1ca..3ba41148c2a4687a0d2c6a515ab6eb2e8de8c4f8 100644 (file)
 
 /* Registers values */
 #define CRC_CR_RESET            BIT(0)
-#define CRC_CR_REVERSE          (BIT(7) | BIT(6) | BIT(5))
-#define CRC_INIT_DEFAULT        0xFFFFFFFF
+#define CRC_CR_REV_IN_WORD      (BIT(6) | BIT(5))
+#define CRC_CR_REV_IN_BYTE      BIT(5)
+#define CRC_CR_REV_OUT          BIT(7)
+#define CRC32C_INIT_DEFAULT     0xFFFFFFFF
 
 #define CRC_AUTOSUSPEND_DELAY  50
 
+static unsigned int burst_size;
+module_param(burst_size, uint, 0644);
+MODULE_PARM_DESC(burst_size, "Select burst byte size (0 unlimited)");
+
 struct stm32_crc {
        struct list_head list;
        struct device    *dev;
        void __iomem     *regs;
        struct clk       *clk;
-       u8               pending_data[sizeof(u32)];
-       size_t           nb_pending_bytes;
+       spinlock_t       lock;
 };
 
 struct stm32_crc_list {
@@ -59,14 +64,13 @@ struct stm32_crc_ctx {
 
 struct stm32_crc_desc_ctx {
        u32    partial; /* crc32c: partial in first 4 bytes of that struct */
-       struct stm32_crc *crc;
 };
 
 static int stm32_crc32_cra_init(struct crypto_tfm *tfm)
 {
        struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm);
 
-       mctx->key = CRC_INIT_DEFAULT;
+       mctx->key = 0;
        mctx->poly = CRC32_POLY_LE;
        return 0;
 }
@@ -75,7 +79,7 @@ static int stm32_crc32c_cra_init(struct crypto_tfm *tfm)
 {
        struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm);
 
-       mctx->key = CRC_INIT_DEFAULT;
+       mctx->key = CRC32C_INIT_DEFAULT;
        mctx->poly = CRC32C_POLY_LE;
        return 0;
 }
@@ -92,87 +96,135 @@ static int stm32_crc_setkey(struct crypto_shash *tfm, const u8 *key,
        return 0;
 }
 
+static struct stm32_crc *stm32_crc_get_next_crc(void)
+{
+       struct stm32_crc *crc;
+
+       spin_lock_bh(&crc_list.lock);
+       crc = list_first_entry(&crc_list.dev_list, struct stm32_crc, list);
+       if (crc)
+               list_move_tail(&crc->list, &crc_list.dev_list);
+       spin_unlock_bh(&crc_list.lock);
+
+       return crc;
+}
+
 static int stm32_crc_init(struct shash_desc *desc)
 {
        struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc);
        struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm);
        struct stm32_crc *crc;
+       unsigned long flags;
 
-       spin_lock_bh(&crc_list.lock);
-       list_for_each_entry(crc, &crc_list.dev_list, list) {
-               ctx->crc = crc;
-               break;
-       }
-       spin_unlock_bh(&crc_list.lock);
+       crc = stm32_crc_get_next_crc();
+       if (!crc)
+               return -ENODEV;
+
+       pm_runtime_get_sync(crc->dev);
 
-       pm_runtime_get_sync(ctx->crc->dev);
+       spin_lock_irqsave(&crc->lock, flags);
 
        /* Reset, set key, poly and configure in bit reverse mode */
-       writel_relaxed(bitrev32(mctx->key), ctx->crc->regs + CRC_INIT);
-       writel_relaxed(bitrev32(mctx->poly), ctx->crc->regs + CRC_POL);
-       writel_relaxed(CRC_CR_RESET | CRC_CR_REVERSE, ctx->crc->regs + CRC_CR);
+       writel_relaxed(bitrev32(mctx->key), crc->regs + CRC_INIT);
+       writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL);
+       writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
+                      crc->regs + CRC_CR);
 
        /* Store partial result */
-       ctx->partial = readl_relaxed(ctx->crc->regs + CRC_DR);
-       ctx->crc->nb_pending_bytes = 0;
+       ctx->partial = readl_relaxed(crc->regs + CRC_DR);
 
-       pm_runtime_mark_last_busy(ctx->crc->dev);
-       pm_runtime_put_autosuspend(ctx->crc->dev);
+       spin_unlock_irqrestore(&crc->lock, flags);
+
+       pm_runtime_mark_last_busy(crc->dev);
+       pm_runtime_put_autosuspend(crc->dev);
 
        return 0;
 }
 
-static int stm32_crc_update(struct shash_desc *desc, const u8 *d8,
-                           unsigned int length)
+static int burst_update(struct shash_desc *desc, const u8 *d8,
+                       size_t length)
 {
        struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc);
-       struct stm32_crc *crc = ctx->crc;
-       u32 *d32;
-       unsigned int i;
+       struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm);
+       struct stm32_crc *crc;
+       unsigned long flags;
+
+       crc = stm32_crc_get_next_crc();
+       if (!crc)
+               return -ENODEV;
 
        pm_runtime_get_sync(crc->dev);
 
-       if (unlikely(crc->nb_pending_bytes)) {
-               while (crc->nb_pending_bytes != sizeof(u32) && length) {
-                       /* Fill in pending data */
-                       crc->pending_data[crc->nb_pending_bytes++] = *(d8++);
+       spin_lock_irqsave(&crc->lock, flags);
+
+       /*
+        * Restore previously calculated CRC for this context as init value
+        * Restore polynomial configuration
+        * Configure in register for word input data,
+        * Configure out register in reversed bit mode data.
+        */
+       writel_relaxed(bitrev32(ctx->partial), crc->regs + CRC_INIT);
+       writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL);
+       writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
+                      crc->regs + CRC_CR);
+
+       if (d8 != PTR_ALIGN(d8, sizeof(u32))) {
+               /* Configure for byte data */
+               writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT,
+                              crc->regs + CRC_CR);
+               while (d8 != PTR_ALIGN(d8, sizeof(u32)) && length) {
+                       writeb_relaxed(*d8++, crc->regs + CRC_DR);
                        length--;
                }
-
-               if (crc->nb_pending_bytes == sizeof(u32)) {
-                       /* Process completed pending data */
-                       writel_relaxed(*(u32 *)crc->pending_data,
-                                      crc->regs + CRC_DR);
-                       crc->nb_pending_bytes = 0;
-               }
+               /* Configure for word data */
+               writel_relaxed(CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
+                              crc->regs + CRC_CR);
        }
 
-       d32 = (u32 *)d8;
-       for (i = 0; i < length >> 2; i++)
-               /* Process 32 bits data */
-               writel_relaxed(*(d32++), crc->regs + CRC_DR);
+       for (; length >= sizeof(u32); d8 += sizeof(u32), length -= sizeof(u32))
+               writel_relaxed(*((u32 *)d8), crc->regs + CRC_DR);
+
+       if (length) {
+               /* Configure for byte data */
+               writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT,
+                              crc->regs + CRC_CR);
+               while (length--)
+                       writeb_relaxed(*d8++, crc->regs + CRC_DR);
+       }
 
        /* Store partial result */
        ctx->partial = readl_relaxed(crc->regs + CRC_DR);
 
+       spin_unlock_irqrestore(&crc->lock, flags);
+
        pm_runtime_mark_last_busy(crc->dev);
        pm_runtime_put_autosuspend(crc->dev);
 
-       /* Check for pending data (non 32 bits) */
-       length &= 3;
-       if (likely(!length))
-               return 0;
+       return 0;
+}
 
-       if ((crc->nb_pending_bytes + length) >= sizeof(u32)) {
-               /* Shall not happen */
-               dev_err(crc->dev, "Pending data overflow\n");
-               return -EINVAL;
-       }
+static int stm32_crc_update(struct shash_desc *desc, const u8 *d8,
+                           unsigned int length)
+{
+       const unsigned int burst_sz = burst_size;
+       unsigned int rem_sz;
+       const u8 *cur;
+       size_t size;
+       int ret;
 
-       d8 = (const u8 *)d32;
-       for (i = 0; i < length; i++)
-               /* Store pending data */
-               crc->pending_data[crc->nb_pending_bytes++] = *(d8++);
+       if (!burst_sz)
+               return burst_update(desc, d8, length);
+
+       /* Digest first bytes not 32bit aligned at first pass in the loop */
+       size = min(length,
+                  burst_sz + (unsigned int)d8 - ALIGN_DOWN((unsigned int)d8,
+                                                           sizeof(u32)));
+       for (rem_sz = length, cur = d8; rem_sz;
+            rem_sz -= size, cur += size, size = min(rem_sz, burst_sz)) {
+               ret = burst_update(desc, cur, size);
+               if (ret)
+                       return ret;
+       }
 
        return 0;
 }
@@ -202,6 +254,8 @@ static int stm32_crc_digest(struct shash_desc *desc, const u8 *data,
        return stm32_crc_init(desc) ?: stm32_crc_finup(desc, data, length, out);
 }
 
+static unsigned int refcnt;
+static DEFINE_MUTEX(refcnt_lock);
 static struct shash_alg algs[] = {
        /* CRC-32 */
        {
@@ -284,20 +338,29 @@ static int stm32_crc_probe(struct platform_device *pdev)
 
        pm_runtime_get_noresume(dev);
        pm_runtime_set_active(dev);
+       pm_runtime_irq_safe(dev);
        pm_runtime_enable(dev);
 
+       spin_lock_init(&crc->lock);
+
        platform_set_drvdata(pdev, crc);
 
        spin_lock(&crc_list.lock);
        list_add(&crc->list, &crc_list.dev_list);
        spin_unlock(&crc_list.lock);
 
-       ret = crypto_register_shashes(algs, ARRAY_SIZE(algs));
-       if (ret) {
-               dev_err(dev, "Failed to register\n");
-               clk_disable_unprepare(crc->clk);
-               return ret;
+       mutex_lock(&refcnt_lock);
+       if (!refcnt) {
+               ret = crypto_register_shashes(algs, ARRAY_SIZE(algs));
+               if (ret) {
+                       mutex_unlock(&refcnt_lock);
+                       dev_err(dev, "Failed to register\n");
+                       clk_disable_unprepare(crc->clk);
+                       return ret;
+               }
        }
+       refcnt++;
+       mutex_unlock(&refcnt_lock);
 
        dev_info(dev, "Initialized\n");
 
@@ -318,7 +381,10 @@ static int stm32_crc_remove(struct platform_device *pdev)
        list_del(&crc->list);
        spin_unlock(&crc_list.lock);
 
-       crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+       mutex_lock(&refcnt_lock);
+       if (!--refcnt)
+               crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+       mutex_unlock(&refcnt_lock);
 
        pm_runtime_disable(crc->dev);
        pm_runtime_put_noidle(crc->dev);
@@ -328,34 +394,60 @@ static int stm32_crc_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int stm32_crc_runtime_suspend(struct device *dev)
+static int __maybe_unused stm32_crc_suspend(struct device *dev)
 {
        struct stm32_crc *crc = dev_get_drvdata(dev);
+       int ret;
 
-       clk_disable_unprepare(crc->clk);
+       ret = pm_runtime_force_suspend(dev);
+       if (ret)
+               return ret;
+
+       clk_unprepare(crc->clk);
 
        return 0;
 }
 
-static int stm32_crc_runtime_resume(struct device *dev)
+static int __maybe_unused stm32_crc_resume(struct device *dev)
 {
        struct stm32_crc *crc = dev_get_drvdata(dev);
        int ret;
 
-       ret = clk_prepare_enable(crc->clk);
+       ret = clk_prepare(crc->clk);
        if (ret) {
-               dev_err(crc->dev, "Failed to prepare_enable clock\n");
+               dev_err(crc->dev, "Failed to prepare clock\n");
+               return ret;
+       }
+
+       return pm_runtime_force_resume(dev);
+}
+
+static int __maybe_unused stm32_crc_runtime_suspend(struct device *dev)
+{
+       struct stm32_crc *crc = dev_get_drvdata(dev);
+
+       clk_disable(crc->clk);
+
+       return 0;
+}
+
+static int __maybe_unused stm32_crc_runtime_resume(struct device *dev)
+{
+       struct stm32_crc *crc = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_enable(crc->clk);
+       if (ret) {
+               dev_err(crc->dev, "Failed to enable clock\n");
                return ret;
        }
 
        return 0;
 }
-#endif
 
 static const struct dev_pm_ops stm32_crc_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-                               pm_runtime_force_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(stm32_crc_suspend,
+                               stm32_crc_resume)
        SET_RUNTIME_PM_OPS(stm32_crc_runtime_suspend,
                           stm32_crc_runtime_resume, NULL)
 };
index 167b80eec437f40f564c86848e17897b2086a99f..03c5e66838057595d1448c3e0981485d7dafe706 100644 (file)
@@ -507,6 +507,7 @@ static int stm32_hash_hmac_dma_send(struct stm32_hash_dev *hdev)
 static int stm32_hash_dma_init(struct stm32_hash_dev *hdev)
 {
        struct dma_slave_config dma_conf;
+       struct dma_chan *chan;
        int err;
 
        memset(&dma_conf, 0, sizeof(dma_conf));
@@ -518,11 +519,11 @@ static int stm32_hash_dma_init(struct stm32_hash_dev *hdev)
        dma_conf.dst_maxburst = hdev->dma_maxburst;
        dma_conf.device_fc = false;
 
-       hdev->dma_lch = dma_request_chan(hdev->dev, "in");
-       if (IS_ERR(hdev->dma_lch)) {
-               dev_err(hdev->dev, "Couldn't acquire a slave DMA channel.\n");
-               return PTR_ERR(hdev->dma_lch);
-       }
+       chan = dma_request_chan(hdev->dev, "in");
+       if (IS_ERR(chan))
+               return PTR_ERR(chan);
+
+       hdev->dma_lch = chan;
 
        err = dmaengine_slave_config(hdev->dma_lch, &dma_conf);
        if (err) {
@@ -1463,8 +1464,11 @@ static int stm32_hash_probe(struct platform_device *pdev)
 
        hdev->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(hdev->clk)) {
-               dev_err(dev, "failed to get clock for hash (%lu)\n",
-                       PTR_ERR(hdev->clk));
+               if (PTR_ERR(hdev->clk) != -EPROBE_DEFER) {
+                       dev_err(dev, "failed to get clock for hash (%lu)\n",
+                               PTR_ERR(hdev->clk));
+               }
+
                return PTR_ERR(hdev->clk);
        }
 
@@ -1482,7 +1486,12 @@ static int stm32_hash_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
 
        hdev->rst = devm_reset_control_get(&pdev->dev, NULL);
-       if (!IS_ERR(hdev->rst)) {
+       if (IS_ERR(hdev->rst)) {
+               if (PTR_ERR(hdev->rst) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
+                       goto err_reset;
+               }
+       } else {
                reset_control_assert(hdev->rst);
                udelay(2);
                reset_control_deassert(hdev->rst);
@@ -1493,8 +1502,15 @@ static int stm32_hash_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, hdev);
 
        ret = stm32_hash_dma_init(hdev);
-       if (ret)
+       switch (ret) {
+       case 0:
+               break;
+       case -ENOENT:
                dev_dbg(dev, "DMA mode not available\n");
+               break;
+       default:
+               goto err_dma;
+       }
 
        spin_lock(&stm32_hash.lock);
        list_add_tail(&hdev->list, &stm32_hash.dev_list);
@@ -1532,10 +1548,10 @@ err_engine:
        spin_lock(&stm32_hash.lock);
        list_del(&hdev->list);
        spin_unlock(&stm32_hash.lock);
-
+err_dma:
        if (hdev->dma_lch)
                dma_release_channel(hdev->dma_lch);
-
+err_reset:
        pm_runtime_disable(dev);
        pm_runtime_put_noidle(dev);
 
index 1af823b2fe6bdabea264d17cdbb49670edf61717..4c0af2eb7e19648aecf4489eebd08a60d7d2205d 100644 (file)
@@ -377,6 +377,7 @@ static int dax_open(struct inode *inode, struct file *filp)
        inode->i_mapping->a_ops = &dev_dax_aops;
        filp->f_mapping = inode->i_mapping;
        filp->f_wb_err = filemap_sample_wb_err(filp->f_mapping);
+       filp->f_sb_err = file_sample_sb_err(filp);
        filp->private_data = dev_dax;
        inode->i_flags = S_DAX;
 
index 0b1df12e0f21ce57ec5c3108095b3e60d5178b6e..37dc40d1fcfbbe89cf5c4ea136e2a8a7c5188f3e 100644 (file)
@@ -91,6 +91,14 @@ config ARM_EXYNOS_BUS_DEVFREQ
          and adjusts the operating frequencies and voltages with OPP support.
          This does not yet operate with optimal voltages.
 
+config ARM_IMX_BUS_DEVFREQ
+       tristate "i.MX Generic Bus DEVFREQ Driver"
+       depends on ARCH_MXC || COMPILE_TEST
+       select DEVFREQ_GOV_USERSPACE
+       help
+         This adds the generic DEVFREQ driver for i.MX interconnects. It
+         allows adjusting NIC/NOC frequency.
+
 config ARM_IMX8M_DDRC_DEVFREQ
        tristate "i.MX8M DDRC DEVFREQ Driver"
        depends on (ARCH_MXC && HAVE_ARM_SMCCC) || \
index 3eb4d5e6635c602a857287c1b5c7614439203347..3ca1ad0ecb97f21486f04e34445f1f6ca14bb202 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)       += governor_passive.o
 
 # DEVFREQ Drivers
 obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)   += exynos-bus.o
+obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ)      += imx-bus.o
 obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ)   += imx8m-ddrc.o
 obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)   += rk3399_dmc.o
 obj-$(CONFIG_ARM_TEGRA_DEVFREQ)                += tegra30-devfreq.o
index 6fecd11dafddafa99c1e4bcf28fc485192bc884f..52b9c3e141f37d882b9913e75db90b7f776f5096 100644 (file)
@@ -60,12 +60,12 @@ static struct devfreq *find_device_devfreq(struct device *dev)
 {
        struct devfreq *tmp_devfreq;
 
+       lockdep_assert_held(&devfreq_list_lock);
+
        if (IS_ERR_OR_NULL(dev)) {
                pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
                return ERR_PTR(-EINVAL);
        }
-       WARN(!mutex_is_locked(&devfreq_list_lock),
-            "devfreq_list_lock must be locked.");
 
        list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
                if (tmp_devfreq->dev.parent == dev)
@@ -258,12 +258,12 @@ static struct devfreq_governor *find_devfreq_governor(const char *name)
 {
        struct devfreq_governor *tmp_governor;
 
+       lockdep_assert_held(&devfreq_list_lock);
+
        if (IS_ERR_OR_NULL(name)) {
                pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
                return ERR_PTR(-EINVAL);
        }
-       WARN(!mutex_is_locked(&devfreq_list_lock),
-            "devfreq_list_lock must be locked.");
 
        list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
                if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
@@ -289,12 +289,12 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
        struct devfreq_governor *governor;
        int err = 0;
 
+       lockdep_assert_held(&devfreq_list_lock);
+
        if (IS_ERR_OR_NULL(name)) {
                pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
                return ERR_PTR(-EINVAL);
        }
-       WARN(!mutex_is_locked(&devfreq_list_lock),
-            "devfreq_list_lock must be locked.");
 
        governor = find_devfreq_governor(name);
        if (IS_ERR(governor)) {
@@ -392,10 +392,7 @@ int update_devfreq(struct devfreq *devfreq)
        int err = 0;
        u32 flags = 0;
 
-       if (!mutex_is_locked(&devfreq->lock)) {
-               WARN(true, "devfreq->lock must be locked by the caller.\n");
-               return -EINVAL;
-       }
+       lockdep_assert_held(&devfreq->lock);
 
        if (!devfreq->governor)
                return -EINVAL;
@@ -768,7 +765,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
        devfreq->dev.release = devfreq_dev_release;
        INIT_LIST_HEAD(&devfreq->node);
        devfreq->profile = profile;
-       strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
+       strscpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
        devfreq->previous_freq = profile->initial_freq;
        devfreq->last_status.current_frequency = profile->initial_freq;
        devfreq->data = data;
diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c
new file mode 100644 (file)
index 0000000..4f38455
--- /dev/null
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/devfreq.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct imx_bus {
+       struct devfreq_dev_profile profile;
+       struct devfreq *devfreq;
+       struct clk *clk;
+       struct platform_device *icc_pdev;
+};
+
+static int imx_bus_target(struct device *dev,
+               unsigned long *freq, u32 flags)
+{
+       struct dev_pm_opp *new_opp;
+       int ret;
+
+       new_opp = devfreq_recommended_opp(dev, freq, flags);
+       if (IS_ERR(new_opp)) {
+               ret = PTR_ERR(new_opp);
+               dev_err(dev, "failed to get recommended opp: %d\n", ret);
+               return ret;
+       }
+       dev_pm_opp_put(new_opp);
+
+       return dev_pm_opp_set_rate(dev, *freq);
+}
+
+static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+       struct imx_bus *priv = dev_get_drvdata(dev);
+
+       *freq = clk_get_rate(priv->clk);
+
+       return 0;
+}
+
+static int imx_bus_get_dev_status(struct device *dev,
+               struct devfreq_dev_status *stat)
+{
+       struct imx_bus *priv = dev_get_drvdata(dev);
+
+       stat->busy_time = 0;
+       stat->total_time = 0;
+       stat->current_frequency = clk_get_rate(priv->clk);
+
+       return 0;
+}
+
+static void imx_bus_exit(struct device *dev)
+{
+       struct imx_bus *priv = dev_get_drvdata(dev);
+
+       dev_pm_opp_of_remove_table(dev);
+       platform_device_unregister(priv->icc_pdev);
+}
+
+/* imx_bus_init_icc() - register matching icc provider if required */
+static int imx_bus_init_icc(struct device *dev)
+{
+       struct imx_bus *priv = dev_get_drvdata(dev);
+       const char *icc_driver_name;
+
+       if (!of_get_property(dev->of_node, "#interconnect-cells", 0))
+               return 0;
+       if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
+               dev_warn(dev, "imx interconnect drivers disabled\n");
+               return 0;
+       }
+
+       icc_driver_name = of_device_get_match_data(dev);
+       if (!icc_driver_name) {
+               dev_err(dev, "unknown interconnect driver\n");
+               return 0;
+       }
+
+       priv->icc_pdev = platform_device_register_data(
+                       dev, icc_driver_name, -1, NULL, 0);
+       if (IS_ERR(priv->icc_pdev)) {
+               dev_err(dev, "failed to register icc provider %s: %ld\n",
+                               icc_driver_name, PTR_ERR(priv->icc_pdev));
+               return PTR_ERR(priv->icc_pdev);
+       }
+
+       return 0;
+}
+
+static int imx_bus_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct imx_bus *priv;
+       const char *gov = DEVFREQ_GOV_USERSPACE;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /*
+        * Fetch the clock to adjust but don't explicitly enable.
+        *
+        * For imx bus clock clk_set_rate is safe no matter if the clock is on
+        * or off and some peripheral side-buses might be off unless enabled by
+        * drivers for devices on those specific buses.
+        *
+        * Rate adjustment on a disabled bus clock just takes effect later.
+        */
+       priv->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               ret = PTR_ERR(priv->clk);
+               dev_err(dev, "failed to fetch clk: %d\n", ret);
+               return ret;
+       }
+       platform_set_drvdata(pdev, priv);
+
+       ret = dev_pm_opp_of_add_table(dev);
+       if (ret < 0) {
+               dev_err(dev, "failed to get OPP table\n");
+               return ret;
+       }
+
+       priv->profile.polling_ms = 1000;
+       priv->profile.target = imx_bus_target;
+       priv->profile.get_dev_status = imx_bus_get_dev_status;
+       priv->profile.exit = imx_bus_exit;
+       priv->profile.get_cur_freq = imx_bus_get_cur_freq;
+       priv->profile.initial_freq = clk_get_rate(priv->clk);
+
+       priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
+                                               gov, NULL);
+       if (IS_ERR(priv->devfreq)) {
+               ret = PTR_ERR(priv->devfreq);
+               dev_err(dev, "failed to add devfreq device: %d\n", ret);
+               goto err;
+       }
+
+       ret = imx_bus_init_icc(dev);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       dev_pm_opp_of_remove_table(dev);
+       return ret;
+}
+
+static const struct of_device_id imx_bus_of_match[] = {
+       { .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", },
+       { .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", },
+       { .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", },
+       { .compatible = "fsl,imx8m-noc", },
+       { .compatible = "fsl,imx8m-nic", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_bus_of_match);
+
+static struct platform_driver imx_bus_platdrv = {
+       .probe          = imx_bus_probe,
+       .driver = {
+               .name   = "imx-bus-devfreq",
+               .of_match_table = of_match_ptr(imx_bus_of_match),
+       },
+};
+module_platform_driver(imx_bus_platdrv);
+
+MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver");
+MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
+MODULE_LICENSE("GPL v2");
index 28b2c7ca416e092497c6fdcfe3775e76840fb57e..e94a27804c209a28aaf724afd3d89b1675328746 100644 (file)
@@ -420,7 +420,7 @@ tegra_actmon_cpufreq_contribution(struct tegra_devfreq *tegra,
 
        static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
 
-       if (dev_freq >= static_cpu_emc_freq)
+       if (dev_freq + actmon_dev->boost_freq >= static_cpu_emc_freq)
                return 0;
 
        return static_cpu_emc_freq;
@@ -807,10 +807,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
        }
 
        err = platform_get_irq(pdev, 0);
-       if (err < 0) {
-               dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err);
+       if (err < 0)
                return err;
-       }
+
        tegra->irq = err;
 
        irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN);
index f91f3bc1e0b28b1095977ce3b27621e6fc08616f..9cf7cc1f3f72fc4a3ecc0a68e4a6245d1effcba1 100644 (file)
@@ -3403,7 +3403,7 @@ static const struct attribute_group *amd64_edac_attr_groups[] = {
 static int hw_info_get(struct amd64_pvt *pvt)
 {
        u16 pci_id1, pci_id2;
-       int ret = -EINVAL;
+       int ret;
 
        if (pvt->fam >= 0x17) {
                pvt->umc = kcalloc(fam_type->max_mcs, sizeof(struct amd64_umc), GFP_KERNEL);
index 93c82bc17493ae073791b96de7874b7e9b62fe95..169353710982643222ff75d986f71e3d582ef8f5 100644 (file)
@@ -44,14 +44,6 @@ static void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32)
                        " PCI Access Write Error at 0x%x\n", reg);
 }
 
-static char * const bridge_str[] = {
-       [NORTH_A] = "NORTH A",
-       [NORTH_B] = "NORTH B",
-       [SOUTH_A] = "SOUTH A",
-       [SOUTH_B] = "SOUTH B",
-       [NO_BRIDGE] = "NO BRIDGE",
-};
-
 /* Support up to two AMD8131 chipsets on a platform */
 static struct amd8131_dev_info amd8131_devices[] = {
        {
index a7502ebe9bdc529c1ed17ab827f1a3321bf0aab1..e3e757513d1bc38eee01eb0c6836ecb8a63bd9a3 100644 (file)
@@ -78,7 +78,7 @@ struct axp_mc_drvdata {
        char msg[128];
 };
 
-/* derived from "DRAM Address Multiplexing" in the ARAMDA XP Functional Spec */
+/* derived from "DRAM Address Multiplexing" in the ARMADA XP Functional Spec */
 static uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata,
                                    uint8_t cs, uint8_t bank, uint16_t row,
                                    uint16_t col)
@@ -160,12 +160,12 @@ static void axp_mc_check(struct mem_ctl_info *mci)
                if (cnt_sbe)
                        cnt_sbe--;
                else
-                       dev_warn(mci->pdev, "inconsistent SBE count detected");
+                       dev_warn(mci->pdev, "inconsistent SBE count detected\n");
        } else {
                if (cnt_dbe)
                        cnt_dbe--;
                else
-                       dev_warn(mci->pdev, "inconsistent DBE count detected");
+                       dev_warn(mci->pdev, "inconsistent DBE count detected\n");
        }
 
        /* report earlier errors */
@@ -304,7 +304,7 @@ static int axp_mc_probe(struct platform_device *pdev)
 
        config = readl(base + SDRAM_CONFIG_REG);
        if (!(config & SDRAM_CONFIG_ECC_MASK)) {
-               dev_warn(&pdev->dev, "SDRAM ECC is not enabled");
+               dev_warn(&pdev->dev, "SDRAM ECC is not enabled\n");
                return -EINVAL;
        }
 
@@ -532,9 +532,9 @@ static int aurora_l2_probe(struct platform_device *pdev)
 
        l2x0_aux_ctrl = readl(base + L2X0_AUX_CTRL);
        if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN))
-               dev_warn(&pdev->dev, "tag parity is not enabled");
+               dev_warn(&pdev->dev, "tag parity is not enabled\n");
        if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN))
-               dev_warn(&pdev->dev, "data ECC is not enabled");
+               dev_warn(&pdev->dev, "data ECC is not enabled\n");
 
        dci = edac_device_alloc_ctl_info(sizeof(*drvdata),
                                         "cpu", 1, "L", 1, 2, NULL, 0, 0);
@@ -618,7 +618,7 @@ static int __init armada_xp_edac_init(void)
 
        res = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
        if (res)
-               pr_warn("Aramda XP EDAC drivers fail to register\n");
+               pr_warn("Armada XP EDAC drivers fail to register\n");
 
        return 0;
 }
index df08de963d1017607de5df515108e445bf3cd79e..9b0044cd21cd57db08db89d0bbb337435785ff0f 100644 (file)
@@ -122,10 +122,22 @@ static int i10nm_get_all_munits(void)
        return 0;
 }
 
+static struct res_config i10nm_cfg0 = {
+       .type                   = I10NM,
+       .decs_did               = 0x3452,
+       .busno_cfg_offset       = 0xcc,
+};
+
+static struct res_config i10nm_cfg1 = {
+       .type                   = I10NM,
+       .decs_did               = 0x3452,
+       .busno_cfg_offset       = 0xd0,
+};
+
 static const struct x86_cpu_id i10nm_cpuids[] = {
-       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,           NULL),
-       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,           NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      &i10nm_cfg0),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,           &i10nm_cfg0),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,           &i10nm_cfg1),
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids);
@@ -161,7 +173,7 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci)
                                 mtr, mcddrtcfg, imc->mc, i, j);
 
                        if (IS_DIMM_PRESENT(mtr))
-                               ndimms += skx_get_dimm_info(mtr, 0, dimm,
+                               ndimms += skx_get_dimm_info(mtr, 0, 0, dimm,
                                                            imc, i, j);
                        else if (IS_NVDIMM_PRESENT(mcddrtcfg, j))
                                ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
@@ -234,6 +246,7 @@ static int __init i10nm_init(void)
 {
        u8 mc = 0, src_id = 0, node_id = 0;
        const struct x86_cpu_id *id;
+       struct res_config *cfg;
        const char *owner;
        struct skx_dev *d;
        int rc, i, off[3] = {0xd0, 0xc8, 0xcc};
@@ -249,11 +262,17 @@ static int __init i10nm_init(void)
        if (!id)
                return -ENODEV;
 
+       cfg = (struct res_config *)id->driver_data;
+
+       /* Newer steppings have different offset for ATOM_TREMONT_D/ICELAKE_X */
+       if (boot_cpu_data.x86_stepping >= 4)
+               cfg->busno_cfg_offset = 0xd0;
+
        rc = skx_get_hi_lo(0x09a2, off, &tolm, &tohm);
        if (rc)
                return rc;
 
-       rc = skx_get_all_bus_mappings(0x3452, 0xcc, I10NM, &i10nm_edac_list);
+       rc = skx_get_all_bus_mappings(cfg, &i10nm_edac_list);
        if (rc < 0)
                goto fail;
        if (rc == 0) {
index 46a3a3440f5e7f1225b161ca38fc9bb5728d8e6c..b907a0f4ece6cadcd0be10bec8f39e667664e7a4 100644 (file)
@@ -157,33 +157,35 @@ fail:
        return -ENODEV;
 }
 
+static struct res_config skx_cfg = {
+       .type                   = SKX,
+       .decs_did               = 0x2016,
+       .busno_cfg_offset       = 0xcc,
+};
+
 static const struct x86_cpu_id skx_cpuids[] = {
-       X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X,   NULL),
+       X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X,   &skx_cfg),
        { }
 };
 MODULE_DEVICE_TABLE(x86cpu, skx_cpuids);
 
-#define SKX_GET_MTMTR(dev, reg) \
-       pci_read_config_dword((dev), 0x87c, &(reg))
-
-static bool skx_check_ecc(struct pci_dev *pdev)
+static bool skx_check_ecc(u32 mcmtr)
 {
-       u32 mtmtr;
-
-       SKX_GET_MTMTR(pdev, mtmtr);
-
-       return !!GET_BITFIELD(mtmtr, 2, 2);
+       return !!GET_BITFIELD(mcmtr, 2, 2);
 }
 
 static int skx_get_dimm_config(struct mem_ctl_info *mci)
 {
        struct skx_pvt *pvt = mci->pvt_info;
+       u32 mtr, mcmtr, amap, mcddrtcfg;
        struct skx_imc *imc = pvt->imc;
-       u32 mtr, amap, mcddrtcfg;
        struct dimm_info *dimm;
        int i, j;
        int ndimms;
 
+       /* Only the mcmtr on the first channel is effective */
+       pci_read_config_dword(imc->chan[0].cdev, 0x87c, &mcmtr);
+
        for (i = 0; i < SKX_NUM_CHANNELS; i++) {
                ndimms = 0;
                pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap);
@@ -193,14 +195,14 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci)
                        pci_read_config_dword(imc->chan[i].cdev,
                                              0x80 + 4 * j, &mtr);
                        if (IS_DIMM_PRESENT(mtr)) {
-                               ndimms += skx_get_dimm_info(mtr, amap, dimm, imc, i, j);
+                               ndimms += skx_get_dimm_info(mtr, mcmtr, amap, dimm, imc, i, j);
                        } else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) {
                                ndimms += skx_get_nvdimm_info(dimm, imc, i, j,
                                                              EDAC_MOD_STR);
                                nvdimm_count++;
                        }
                }
-               if (ndimms && !skx_check_ecc(imc->chan[0].cdev)) {
+               if (ndimms && !skx_check_ecc(mcmtr)) {
                        skx_printk(KERN_ERR, "ECC is disabled on imc %d\n", imc->mc);
                        return -ENODEV;
                }
@@ -641,6 +643,7 @@ static inline void teardown_skx_debug(void) {}
 static int __init skx_init(void)
 {
        const struct x86_cpu_id *id;
+       struct res_config *cfg;
        const struct munit *m;
        const char *owner;
        int rc = 0, i, off[3] = {0xd0, 0xd4, 0xd8};
@@ -657,11 +660,13 @@ static int __init skx_init(void)
        if (!id)
                return -ENODEV;
 
+       cfg = (struct res_config *)id->driver_data;
+
        rc = skx_get_hi_lo(0x2034, off, &skx_tolm, &skx_tohm);
        if (rc)
                return rc;
 
-       rc = skx_get_all_bus_mappings(0x2016, 0xcc, SKX, &skx_edac_list);
+       rc = skx_get_all_bus_mappings(cfg, &skx_edac_list);
        if (rc < 0)
                goto fail;
        if (rc == 0) {
index 99bbaf629b8d90ee18a321b68bac0edd55f62049..46be1a77bd1d723897dc42921c7cbf22d3382d3d 100644 (file)
@@ -197,12 +197,11 @@ static int get_width(u32 mtr)
 }
 
 /*
- * We use the per-socket device @did to count how many sockets are present,
+ * We use the per-socket device @cfg->did to count how many sockets are present,
  * and to detemine which PCI buses are associated with each socket. Allocate
  * and build the full list of all the skx_dev structures that we need here.
  */
-int skx_get_all_bus_mappings(unsigned int did, int off, enum type type,
-                            struct list_head **list)
+int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list)
 {
        struct pci_dev *pdev, *prev;
        struct skx_dev *d;
@@ -211,7 +210,7 @@ int skx_get_all_bus_mappings(unsigned int did, int off, enum type type,
 
        prev = NULL;
        for (;;) {
-               pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, prev);
+               pdev = pci_get_device(PCI_VENDOR_ID_INTEL, cfg->decs_did, prev);
                if (!pdev)
                        break;
                ndev++;
@@ -221,7 +220,7 @@ int skx_get_all_bus_mappings(unsigned int did, int off, enum type type,
                        return -ENOMEM;
                }
 
-               if (pci_read_config_dword(pdev, off, &reg)) {
+               if (pci_read_config_dword(pdev, cfg->busno_cfg_offset, &reg)) {
                        kfree(d);
                        pci_dev_put(pdev);
                        skx_printk(KERN_ERR, "Failed to read bus idx\n");
@@ -230,7 +229,7 @@ int skx_get_all_bus_mappings(unsigned int did, int off, enum type type,
 
                d->bus[0] = GET_BITFIELD(reg, 0, 7);
                d->bus[1] = GET_BITFIELD(reg, 8, 15);
-               if (type == SKX) {
+               if (cfg->type == SKX) {
                        d->seg = pci_domain_nr(pdev->bus);
                        d->bus[2] = GET_BITFIELD(reg, 16, 23);
                        d->bus[3] = GET_BITFIELD(reg, 24, 31);
@@ -304,7 +303,7 @@ static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add,
 #define numrow(reg)    skx_get_dimm_attr(reg, 2, 4, 12, 1, 6, "rows")
 #define numcol(reg)    skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols")
 
-int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
+int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
                      struct skx_imc *imc, int chan, int dimmno)
 {
        int  banks = 16, ranks, rows, cols, npages;
@@ -324,8 +323,8 @@ int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
                 imc->mc, chan, dimmno, size, npages,
                 banks, 1 << ranks, rows, cols);
 
-       imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mtr, 0, 0);
-       imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mtr, 9, 9);
+       imc->chan[chan].dimms[dimmno].close_pg = GET_BITFIELD(mcmtr, 0, 0);
+       imc->chan[chan].dimms[dimmno].bank_xor_enable = GET_BITFIELD(mcmtr, 9, 9);
        imc->chan[chan].dimms[dimmno].fine_grain_bank = GET_BITFIELD(amap, 0, 0);
        imc->chan[chan].dimms[dimmno].rowbits = rows;
        imc->chan[chan].dimms[dimmno].colbits = cols;
index 60d1ea669afd49e46b0212cc2fe5436fabbc4b40..78f8c1de0b71c80ddcdb7d64fc4bd63a064d0f3f 100644 (file)
@@ -112,6 +112,14 @@ struct decoded_addr {
        int     bank_group;
 };
 
+struct res_config {
+       enum type type;
+       /* Configuration agent device ID */
+       unsigned int decs_did;
+       /* Default bus number configuration register offset */
+       int busno_cfg_offset;
+};
+
 typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci);
 typedef bool (*skx_decode_f)(struct decoded_addr *res);
 typedef void (*skx_show_retry_log_f)(struct decoded_addr *res, char *msg, int len);
@@ -123,12 +131,11 @@ void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log);
 int skx_get_src_id(struct skx_dev *d, int off, u8 *id);
 int skx_get_node_id(struct skx_dev *d, u8 *id);
 
-int skx_get_all_bus_mappings(unsigned int did, int off, enum type,
-                            struct list_head **list);
+int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list);
 
 int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm);
 
-int skx_get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm,
+int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
                      struct skx_imc *imc, int chan, int dimmno);
 
 int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
index 34be60fe68922100bac7f2ae4a7dc1b50c8ad419..4af9744cc6d0212389e7a8f8b71fc3741f78db1f 100644 (file)
@@ -1278,7 +1278,7 @@ OCX_DEBUGFS_ATTR(lne23_badcnt, OCX_LNE_BAD_CNT(23));
 
 OCX_DEBUGFS_ATTR(com_int, OCX_COM_INT_W1S);
 
-struct debugfs_entry *ocx_dfs_ents[] = {
+static struct debugfs_entry *ocx_dfs_ents[] = {
        &debugfs_tlk0_ecc_ctl,
        &debugfs_tlk1_ecc_ctl,
        &debugfs_tlk2_ecc_ctl,
@@ -1919,19 +1919,19 @@ err_free:
 
 L2C_DEBUGFS_ATTR(tad_int, L2C_TAD_INT_W1S);
 
-struct debugfs_entry *l2c_tad_dfs_ents[] = {
+static struct debugfs_entry *l2c_tad_dfs_ents[] = {
        &debugfs_tad_int,
 };
 
 L2C_DEBUGFS_ATTR(cbc_int, L2C_CBC_INT_W1S);
 
-struct debugfs_entry *l2c_cbc_dfs_ents[] = {
+static struct debugfs_entry *l2c_cbc_dfs_ents[] = {
        &debugfs_cbc_int,
 };
 
 L2C_DEBUGFS_ATTR(mci_int, L2C_MCI_INT_W1S);
 
-struct debugfs_entry *l2c_mci_dfs_ents[] = {
+static struct debugfs_entry *l2c_mci_dfs_ents[] = {
        &debugfs_mci_int,
 };
 
index e4a1032ba0b58a4e93dc055e94dce2181c351277..1d2c27a00a4a8348eb5e0d0c203eafa4520c7030 100644 (file)
@@ -1349,7 +1349,6 @@ static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3)
 #define WORD_ALIGNED_ERR_MASK          BIT(28)
 #define PAGE_ACCESS_ERR_MASK           BIT(27)
 #define WRITE_ACCESS_MASK              BIT(26)
-#define RBERRADDR_RD(src)              ((src) & 0x03FFFFFF)
 
 static const char * const soc_mem_err_v1[] = {
        "10GbE0",
@@ -1483,13 +1482,11 @@ static void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev)
                return;
        if (reg & STICKYERR_MASK) {
                bool write;
-               u32 address;
 
                dev_err(edac_dev->dev, "IOB bus access error(s)\n");
                if (regmap_read(ctx->edac->rb_map, RBEIR, &reg))
                        return;
                write = reg & WRITE_ACCESS_MASK ? 1 : 0;
-               address = RBERRADDR_RD(reg);
                if (reg & AGENT_OFFLINE_ERR_MASK)
                        dev_err(edac_dev->dev,
                                "IOB bus %s access to offline agent error\n",
index 6e291d8f3a278556c8d0349b7aed9c8d9e67db9c..c7ea4f2d5ca6967cdd2b9b7e9eb20e8d851b4d7c 100644 (file)
@@ -1081,8 +1081,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
                return -EINVAL;
 
        p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
-       if (!access_ok(p, a->size))
-               return -EFAULT;
 
        end = (void __user *)p + a->size;
        count = 0;
@@ -1120,7 +1118,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
                        &p->header[transmit_header_bytes / 4];
                if (next > end)
                        return -EINVAL;
-               if (__copy_from_user
+               if (copy_from_user
                    (u.packet.header, p->header, transmit_header_bytes))
                        return -EFAULT;
                if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
index 8007d4aa76dcd6d1e64955a2c3df99c889c11173..4843e94713a4defd3bad33b9bc6eb81fd48090c7 100644 (file)
@@ -295,15 +295,13 @@ config TURRIS_MOX_RWTM
          other manufacturing data and also utilize the Entropy Bit Generator
          for hardware random number generation.
 
-config HAVE_ARM_SMCCC
-       bool
-
-source "drivers/firmware/psci/Kconfig"
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
 source "drivers/firmware/imx/Kconfig"
 source "drivers/firmware/meson/Kconfig"
+source "drivers/firmware/psci/Kconfig"
+source "drivers/firmware/smccc/Kconfig"
 source "drivers/firmware/tegra/Kconfig"
 source "drivers/firmware/xilinx/Kconfig"
 
index e9fb838af4dff087e11737af9c493022312d1943..99510be9f5ed9b2d41830a81d8c258e77928effb 100644 (file)
@@ -23,12 +23,13 @@ obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
 obj-$(CONFIG_TURRIS_MOX_RWTM)  += turris-mox-rwtm.o
 
 obj-$(CONFIG_ARM_SCMI_PROTOCOL)        += arm_scmi/
-obj-y                          += psci/
 obj-y                          += broadcom/
 obj-y                          += meson/
 obj-$(CONFIG_GOOGLE_FIRMWARE)  += google/
 obj-$(CONFIG_EFI)              += efi/
 obj-$(CONFIG_UEFI_CPER)                += efi/
 obj-y                          += imx/
+obj-y                          += psci/
+obj-y                          += smccc/
 obj-y                          += tegra/
 obj-y                          += xilinx/
index 334c8be0c11fb8b0828937e7e459fcd6c0355252..e7e36aab2386ff318280a46f283923e4f016ecd3 100644 (file)
@@ -429,7 +429,6 @@ int sdei_event_enable(u32 event_num)
 
        return err;
 }
-EXPORT_SYMBOL(sdei_event_enable);
 
 static int sdei_api_event_disable(u32 event_num)
 {
@@ -471,7 +470,6 @@ int sdei_event_disable(u32 event_num)
 
        return err;
 }
-EXPORT_SYMBOL(sdei_event_disable);
 
 static int sdei_api_event_unregister(u32 event_num)
 {
@@ -533,7 +531,6 @@ int sdei_event_unregister(u32 event_num)
 
        return err;
 }
-EXPORT_SYMBOL(sdei_event_unregister);
 
 /*
  * unregister events, but don't destroy them as they are re-registered by
@@ -643,7 +640,6 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
 
        return err;
 }
-EXPORT_SYMBOL(sdei_event_register);
 
 static int sdei_reregister_event_llocked(struct sdei_event *event)
 {
@@ -1079,26 +1075,9 @@ static struct platform_driver sdei_driver = {
        .probe          = sdei_probe,
 };
 
-static bool __init sdei_present_dt(void)
-{
-       struct device_node *np, *fw_np;
-
-       fw_np = of_find_node_by_name(NULL, "firmware");
-       if (!fw_np)
-               return false;
-
-       np = of_find_matching_node(fw_np, sdei_of_match);
-       if (!np)
-               return false;
-       of_node_put(np);
-
-       return true;
-}
-
 static bool __init sdei_present_acpi(void)
 {
        acpi_status status;
-       struct platform_device *pdev;
        struct acpi_table_header *sdei_table_header;
 
        if (acpi_disabled)
@@ -1113,20 +1092,26 @@ static bool __init sdei_present_acpi(void)
        if (ACPI_FAILURE(status))
                return false;
 
-       pdev = platform_device_register_simple(sdei_driver.driver.name, 0, NULL,
-                                              0);
-       if (IS_ERR(pdev))
-               return false;
+       acpi_put_table(sdei_table_header);
 
        return true;
 }
 
 static int __init sdei_init(void)
 {
-       if (sdei_present_dt() || sdei_present_acpi())
-               platform_driver_register(&sdei_driver);
+       int ret = platform_driver_register(&sdei_driver);
 
-       return 0;
+       if (!ret && sdei_present_acpi()) {
+               struct platform_device *pdev;
+
+               pdev = platform_device_register_simple(sdei_driver.driver.name,
+                                                      0, NULL, 0);
+               if (IS_ERR(pdev))
+                       pr_info("Failed to register ACPI:SDEI platform device %ld\n",
+                               PTR_ERR(pdev));
+       }
+
+       return ret;
 }
 
 /*
@@ -1143,6 +1128,14 @@ int sdei_event_handler(struct pt_regs *regs,
        mm_segment_t orig_addr_limit;
        u32 event_num = arg->event_num;
 
+       /*
+        * Save restore 'fs'.
+        * The architecture's entry code save/restores 'fs' when taking an
+        * exception from the kernel. This ensures addr_limit isn't inherited
+        * if you interrupted something that allowed the uaccess routines to
+        * access kernel memory.
+        * Do the same here because this doesn't come via the same entry code.
+       */
        orig_addr_limit = get_fs();
        set_fs(USER_DS);
 
index 613828d3f106d0624a93583eb47317edc9946e1e..6b38f9e5d203907dbe390c1d60ff3d8c318b25a2 100644 (file)
@@ -106,12 +106,12 @@ config EFI_PARAMS_FROM_FDT
 config EFI_RUNTIME_WRAPPERS
        bool
 
-config EFI_ARMSTUB
+config EFI_GENERIC_STUB
        bool
 
 config EFI_ARMSTUB_DTB_LOADER
        bool "Enable the DTB loader"
-       depends on EFI_ARMSTUB
+       depends on EFI_GENERIC_STUB
        default y
        help
          Select this config option to add support for the dtb= command
@@ -124,6 +124,17 @@ config EFI_ARMSTUB_DTB_LOADER
          functionality for bootloaders that do not have such support
          this option is necessary.
 
+config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
+       bool "Enable the command line initrd loader" if !X86
+       depends on EFI_STUB && (EFI_GENERIC_STUB || X86)
+       default y
+       help
+         Select this config option to add support for the initrd= command
+         line parameter, allowing an initrd that resides on the same volume
+         as the kernel image to be loaded into memory.
+
+         This method is deprecated.
+
 config EFI_BOOTLOADER_CONTROL
        tristate "EFI Bootloader Control"
        depends on EFI_VARS
index 9e5e62f5f94df4ad47aaae8326787cf8fb1dc73f..c697e70ca7e74e46e1c6996f7c35a14beb881791 100644 (file)
@@ -54,8 +54,8 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
 static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR;
 
 static const efi_config_table_type_t arch_tables[] __initconst = {
-       {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table},
-       {NULL_GUID, NULL, NULL}
+       {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table},
+       {}
 };
 
 static void __init init_screen_info(void)
index 4e3055238f31922d7a59aa0cc2eaa47d3b2b4907..9357d6b6e87cd7453a34bac465a518bc25db7fa5 100644 (file)
@@ -499,21 +499,21 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size)
 }
 
 static const efi_config_table_type_t common_tables[] __initconst = {
-       {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
-       {ACPI_TABLE_GUID, "ACPI", &efi.acpi},
-       {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
-       {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
-       {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
-       {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table},
-       {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi_rng_seed},
-       {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
-       {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log},
-       {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve},
-       {EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop},
+       {ACPI_20_TABLE_GUID,                    &efi.acpi20,            "ACPI 2.0"      },
+       {ACPI_TABLE_GUID,                       &efi.acpi,              "ACPI"          },
+       {SMBIOS_TABLE_GUID,                     &efi.smbios,            "SMBIOS"        },
+       {SMBIOS3_TABLE_GUID,                    &efi.smbios3,           "SMBIOS 3.0"    },
+       {EFI_SYSTEM_RESOURCE_TABLE_GUID,        &efi.esrt,              "ESRT"          },
+       {EFI_MEMORY_ATTRIBUTES_TABLE_GUID,      &efi_mem_attr_table,    "MEMATTR"       },
+       {LINUX_EFI_RANDOM_SEED_TABLE_GUID,      &efi_rng_seed,          "RNG"           },
+       {LINUX_EFI_TPM_EVENT_LOG_GUID,          &efi.tpm_log,           "TPMEventLog"   },
+       {LINUX_EFI_TPM_FINAL_LOG_GUID,          &efi.tpm_final_log,     "TPMFinalLog"   },
+       {LINUX_EFI_MEMRESERVE_TABLE_GUID,       &mem_reserve,           "MEMRESERVE"    },
+       {EFI_RT_PROPERTIES_TABLE_GUID,          &rt_prop,               "RTPROP"        },
 #ifdef CONFIG_EFI_RCI2_TABLE
-       {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys},
+       {DELLEMC_EFI_RCI2_TABLE_GUID,           &rci2_table_phys                        },
 #endif
-       {NULL_GUID, NULL, NULL},
+       {},
 };
 
 static __init int match_config_table(const efi_guid_t *guid,
@@ -522,15 +522,13 @@ static __init int match_config_table(const efi_guid_t *guid,
 {
        int i;
 
-       if (table_types) {
-               for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
-                       if (!efi_guidcmp(*guid, table_types[i].guid)) {
-                               *(table_types[i].ptr) = table;
-                               if (table_types[i].name)
-                                       pr_cont(" %s=0x%lx ",
-                                               table_types[i].name, table);
-                               return 1;
-                       }
+       for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) {
+               if (!efi_guidcmp(*guid, table_types[i].guid)) {
+                       *(table_types[i].ptr) = table;
+                       if (table_types[i].name[0])
+                               pr_cont("%s=0x%lx ",
+                                       table_types[i].name, table);
+                       return 1;
                }
        }
 
@@ -567,7 +565,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables,
                        table = tbl32[i].table;
                }
 
-               if (!match_config_table(guid, table, common_tables))
+               if (!match_config_table(guid, table, common_tables) && arch_tables)
                        match_config_table(guid, table, arch_tables);
        }
        pr_cont("\n");
index 78ad1ba8c987d1cb5b7053869c9416324a468da5..26528a46d99e9e9f0db8c3f55dc3e4706bc774f4 100644 (file)
@@ -522,8 +522,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
        ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
                                   NULL, "%s", short_name);
        kfree(short_name);
-       if (ret)
+       if (ret) {
+               kobject_put(&new_var->kobj);
                return ret;
+       }
 
        kobject_uevent(&new_var->kobj, KOBJ_ADD);
        if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
index 094eabdecfe61fa5a8707d5ec787019b5b42fd3b..cce4a7436052502769d1da47ae724431709cfc71 100644 (file)
@@ -7,7 +7,7 @@
 #
 cflags-$(CONFIG_X86_32)                := -march=i386
 cflags-$(CONFIG_X86_64)                := -mcmodel=small
-cflags-$(CONFIG_X86)           += -m$(BITS) -D__KERNEL__ -O2 \
+cflags-$(CONFIG_X86)           += -m$(BITS) -D__KERNEL__ \
                                   -fPIC -fno-strict-aliasing -mno-red-zone \
                                   -mno-mmx -mno-sse -fshort-wchar \
                                   -Wno-pointer-sign \
@@ -23,15 +23,19 @@ cflags-$(CONFIG_ARM)                := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
                                   -fno-builtin -fpic \
                                   $(call cc-option,-mno-single-pic-base)
 
-cflags-$(CONFIG_EFI_ARMSTUB)   += -I$(srctree)/scripts/dtc/libfdt
+cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
 
-KBUILD_CFLAGS                  := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
+KBUILD_CFLAGS                  := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \
                                   -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \
                                   -D__NO_FORTIFY \
                                   $(call cc-option,-ffreestanding) \
                                   $(call cc-option,-fno-stack-protector) \
+                                  $(call cc-option,-fno-addrsig) \
                                   -D__DISABLE_EXPORTS
 
+# remove SCS flags from all objects in this directory
+KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_SCS), $(KBUILD_CFLAGS))
+
 GCOV_PROFILE                   := n
 KASAN_SANITIZE                 := n
 UBSAN_SANITIZE                 := n
@@ -42,16 +46,17 @@ KCOV_INSTRUMENT                     := n
 
 lib-y                          := efi-stub-helper.o gop.o secureboot.o tpm.o \
                                   file.o mem.o random.o randomalloc.o pci.o \
-                                  skip_spaces.o lib-cmdline.o lib-ctype.o
+                                  skip_spaces.o lib-cmdline.o lib-ctype.o \
+                                  alignedmem.o relocate.o vsprintf.o
 
 # include the stub's generic dependencies from lib/ when building for ARM/arm64
-arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
+efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
 
 $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
        $(call if_changed_rule,cc_o_c)
 
-lib-$(CONFIG_EFI_ARMSTUB)      += arm-stub.o fdt.o string.o \
-                                  $(patsubst %.c,lib-%.o,$(arm-deps-y))
+lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o fdt.o string.o \
+                                  $(patsubst %.c,lib-%.o,$(efi-deps-y))
 
 lib-$(CONFIG_ARM)              += arm32-stub.o
 lib-$(CONFIG_ARM64)            += arm64-stub.o
@@ -59,6 +64,25 @@ lib-$(CONFIG_X86)            += x86-stub.o
 CFLAGS_arm32-stub.o            := -DTEXT_OFFSET=$(TEXT_OFFSET)
 CFLAGS_arm64-stub.o            := -DTEXT_OFFSET=$(TEXT_OFFSET)
 
+#
+# For x86, bootloaders like systemd-boot or grub-efi do not zero-initialize the
+# .bss section, so the .bss section of the EFI stub needs to be included in the
+# .data section of the compressed kernel to ensure initialization. Rename the
+# .bss section here so it's easy to pick out in the linker script.
+#
+STUBCOPY_FLAGS-$(CONFIG_X86)   += --rename-section .bss=.bss.efistub,load,alloc
+STUBCOPY_RELOC-$(CONFIG_X86_32)        := R_386_32
+STUBCOPY_RELOC-$(CONFIG_X86_64)        := R_X86_64_64
+
+#
+# ARM discards the .data section because it disallows r/w data in the
+# decompressor. So move our .data to .data.efistub and .bss to .bss.efistub,
+# which are preserved explicitly by the decompressor linker script.
+#
+STUBCOPY_FLAGS-$(CONFIG_ARM)   += --rename-section .data=.data.efistub \
+                                  --rename-section .bss=.bss.efistub,load,alloc
+STUBCOPY_RELOC-$(CONFIG_ARM)   := R_ARM_ABS
+
 #
 # arm64 puts the stub in the kernel proper, which will unnecessarily retain all
 # code indefinitely unless it is annotated as __init/__initdata/__initconst etc.
@@ -73,8 +97,8 @@ CFLAGS_arm64-stub.o           := -DTEXT_OFFSET=$(TEXT_OFFSET)
 # a verification pass to see if any absolute relocations exist in any of the
 # object files.
 #
-extra-$(CONFIG_EFI_ARMSTUB)    := $(lib-y)
-lib-$(CONFIG_EFI_ARMSTUB)      := $(patsubst %.o,%.stub.o,$(lib-y))
+extra-y                                := $(lib-y)
+lib-y                          := $(patsubst %.o,%.stub.o,$(lib-y))
 
 STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \
                                   --prefix-symbols=__efistub_
@@ -97,11 +121,3 @@ quiet_cmd_stubcopy = STUBCPY $@
                /bin/false;                                             \
        fi;                                                             \
        $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@
-
-#
-# ARM discards the .data section because it disallows r/w data in the
-# decompressor. So move our .data to .data.efistub, which is preserved
-# explicitly by the decompressor linker script.
-#
-STUBCOPY_FLAGS-$(CONFIG_ARM)   += --rename-section .data=.data.efistub
-STUBCOPY_RELOC-$(CONFIG_ARM)   := R_ARM_ABS
diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c
new file mode 100644 (file)
index 0000000..cc89c4d
--- /dev/null
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+/**
+ * efi_allocate_pages_aligned() - Allocate memory pages
+ * @size:      minimum number of bytes to allocate
+ * @addr:      On return the address of the first allocated page. The first
+ *             allocated page has alignment EFI_ALLOC_ALIGN which is an
+ *             architecture dependent multiple of the page size.
+ * @max:       the address that the last allocated memory page shall not
+ *             exceed
+ * @align:     minimum alignment of the base of the allocation
+ *
+ * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according
+ * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will
+ * not exceed the address given by @max.
+ *
+ * Return:     status code
+ */
+efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
+                                       unsigned long max, unsigned long align)
+{
+       efi_physical_addr_t alloc_addr;
+       efi_status_t status;
+       int slack;
+
+       if (align < EFI_ALLOC_ALIGN)
+               align = EFI_ALLOC_ALIGN;
+
+       alloc_addr = ALIGN_DOWN(max + 1, align) - 1;
+       size = round_up(size, EFI_ALLOC_ALIGN);
+       slack = align / EFI_PAGE_SIZE - 1;
+
+       status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
+                            EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
+                            &alloc_addr);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       *addr = ALIGN((unsigned long)alloc_addr, align);
+
+       if (slack > 0) {
+               int l = (alloc_addr % align) / EFI_PAGE_SIZE;
+
+               if (l) {
+                       efi_bs_call(free_pages, alloc_addr, slack - l + 1);
+                       slack = l - 1;
+               }
+               if (slack)
+                       efi_bs_call(free_pages, *addr + size, slack);
+       }
+       return EFI_SUCCESS;
+}
index 7826553af2ba2c4c71c78f6522f8c497049e3f4f..40243f524556cbed96db4f681b13ce9915449a6a 100644 (file)
@@ -18,7 +18,7 @@ efi_status_t check_platform_features(void)
        /* LPAE kernels need compatible hardware */
        block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
        if (block < 5) {
-               pr_efi_err("This LPAE kernel is not supported by your CPU\n");
+               efi_err("This LPAE kernel is not supported by your CPU\n");
                return EFI_UNSUPPORTED;
        }
        return EFI_SUCCESS;
@@ -120,7 +120,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
         */
        status = efi_get_memory_map(&map);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
+               efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
                return status;
        }
 
@@ -162,7 +162,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base,
                                             (end - start) / EFI_PAGE_SIZE,
                                             &start);
                        if (status != EFI_SUCCESS) {
-                               pr_efi_err("reserve_kernel_base(): alloc failed.\n");
+                               efi_err("reserve_kernel_base(): alloc failed.\n");
                                goto out;
                        }
                        break;
@@ -199,14 +199,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
        unsigned long kernel_base;
        efi_status_t status;
 
-       /*
-        * Verify that the DRAM base address is compatible with the ARM
-        * boot protocol, which determines the base of DRAM by masking
-        * off the low 27 bits of the address at which the zImage is
-        * loaded. These assumptions are made by the decompressor,
-        * before any memory map is available.
-        */
-       kernel_base = round_up(dram_base, SZ_128M);
+       /* use a 16 MiB aligned base for the decompressed kernel */
+       kernel_base = round_up(dram_base, SZ_16M) + TEXT_OFFSET;
 
        /*
         * Note that some platforms (notably, the Raspberry Pi 2) put
@@ -215,41 +209,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
         * base of the kernel image is only partially used at the moment.
         * (Up to 5 pages are used for the swapper page tables)
         */
-       kernel_base += TEXT_OFFSET - 5 * PAGE_SIZE;
-
-       status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size);
-       if (status != EFI_SUCCESS) {
-               pr_efi_err("Unable to allocate memory for uncompressed kernel.\n");
-               return status;
-       }
-
-       /*
-        * Relocate the zImage, so that it appears in the lowest 128 MB
-        * memory window.
-        */
-       *image_addr = (unsigned long)image->image_base;
-       *image_size = image->image_size;
-       status = efi_relocate_kernel(image_addr, *image_size, *image_size,
-                                    kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
+       status = reserve_kernel_base(kernel_base - 5 * PAGE_SIZE, reserve_addr,
+                                    reserve_size);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to relocate kernel.\n");
-               efi_free(*reserve_size, *reserve_addr);
-               *reserve_size = 0;
+               efi_err("Unable to allocate memory for uncompressed kernel.\n");
                return status;
        }
 
-       /*
-        * Check to see if we were able to allocate memory low enough
-        * in memory. The kernel determines the base of DRAM from the
-        * address at which the zImage is loaded.
-        */
-       if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
-               pr_efi_err("Failed to relocate kernel, no low memory available.\n");
-               efi_free(*reserve_size, *reserve_addr);
-               *reserve_size = 0;
-               efi_free(*image_size, *image_addr);
-               *image_size = 0;
-               return EFI_LOAD_ERROR;
-       }
+       *image_addr = kernel_base;
+       *image_size = 0;
        return EFI_SUCCESS;
 }
index fc9f8ab533a7eb5cd50f69de47c4444245c2e86a..7f6a57dec51325f9d37cea21d8bd1d00fe0be43d 100644 (file)
@@ -26,14 +26,23 @@ efi_status_t check_platform_features(void)
        tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
        if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) {
                if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
-                       pr_efi_err("This 64 KB granular kernel is not supported by your CPU\n");
+                       efi_err("This 64 KB granular kernel is not supported by your CPU\n");
                else
-                       pr_efi_err("This 16 KB granular kernel is not supported by your CPU\n");
+                       efi_err("This 16 KB granular kernel is not supported by your CPU\n");
                return EFI_UNSUPPORTED;
        }
        return EFI_SUCCESS;
 }
 
+/*
+ * Relocatable kernels can fix up the misalignment with respect to
+ * MIN_KIMG_ALIGN, so they only require a minimum alignment of EFI_KIMG_ALIGN
+ * (which accounts for the alignment of statically allocated objects such as
+ * the swapper stack.)
+ */
+static const u64 min_kimg_align = IS_ENABLED(CONFIG_RELOCATABLE) ? EFI_KIMG_ALIGN
+                                                                : MIN_KIMG_ALIGN;
+
 efi_status_t handle_kernel_image(unsigned long *image_addr,
                                 unsigned long *image_size,
                                 unsigned long *reserve_addr,
@@ -43,106 +52,63 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
 {
        efi_status_t status;
        unsigned long kernel_size, kernel_memsize = 0;
-       unsigned long preferred_offset;
-       u64 phys_seed = 0;
+       u32 phys_seed = 0;
 
        if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
-               if (!nokaslr()) {
+               if (!efi_nokaslr) {
                        status = efi_get_random_bytes(sizeof(phys_seed),
                                                      (u8 *)&phys_seed);
                        if (status == EFI_NOT_FOUND) {
-                               pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
+                               efi_info("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
                        } else if (status != EFI_SUCCESS) {
-                               pr_efi_err("efi_get_random_bytes() failed\n");
+                               efi_err("efi_get_random_bytes() failed\n");
                                return status;
                        }
                } else {
-                       pr_efi("KASLR disabled on kernel command line\n");
+                       efi_info("KASLR disabled on kernel command line\n");
                }
        }
 
-       /*
-        * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
-        * a 2 MB aligned base, which itself may be lower than dram_base, as
-        * long as the resulting offset equals or exceeds it.
-        */
-       preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
-       if (preferred_offset < dram_base)
-               preferred_offset += MIN_KIMG_ALIGN;
+       if (image->image_base != _text)
+               efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
 
        kernel_size = _edata - _text;
        kernel_memsize = kernel_size + (_end - _edata);
+       *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align;
 
        if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
-               /*
-                * Produce a displacement in the interval [0, MIN_KIMG_ALIGN)
-                * that doesn't violate this kernel's de-facto alignment
-                * constraints.
-                */
-               u32 mask = (MIN_KIMG_ALIGN - 1) & ~(EFI_KIMG_ALIGN - 1);
-               u32 offset = (phys_seed >> 32) & mask;
-
-               /*
-                * With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not
-                * be a multiple of EFI_KIMG_ALIGN, and we must ensure that
-                * we preserve the misalignment of 'offset' relative to
-                * EFI_KIMG_ALIGN so that statically allocated objects whose
-                * alignment exceeds PAGE_SIZE appear correctly aligned in
-                * memory.
-                */
-               offset |= TEXT_OFFSET % EFI_KIMG_ALIGN;
-
                /*
                 * If KASLR is enabled, and we have some randomness available,
                 * locate the kernel at a randomized offset in physical memory.
                 */
-               *reserve_size = kernel_memsize + offset;
-               status = efi_random_alloc(*reserve_size,
-                                         MIN_KIMG_ALIGN, reserve_addr,
-                                         (u32)phys_seed);
-
-               *image_addr = *reserve_addr + offset;
+               status = efi_random_alloc(*reserve_size, min_kimg_align,
+                                         reserve_addr, phys_seed);
        } else {
-               /*
-                * Else, try a straight allocation at the preferred offset.
-                * This will work around the issue where, if dram_base == 0x0,
-                * efi_low_alloc() refuses to allocate at 0x0 (to prevent the
-                * address of the allocation to be mistaken for a FAIL return
-                * value or a NULL pointer). It will also ensure that, on
-                * platforms where the [dram_base, dram_base + TEXT_OFFSET)
-                * interval is partially occupied by the firmware (like on APM
-                * Mustang), we can still place the kernel at the address
-                * 'dram_base + TEXT_OFFSET'.
-                */
-               *image_addr = (unsigned long)_text;
-               if (*image_addr == preferred_offset)
-                       return EFI_SUCCESS;
-
-               *image_addr = *reserve_addr = preferred_offset;
-               *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
-
-               status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
-                                    EFI_LOADER_DATA,
-                                    *reserve_size / EFI_PAGE_SIZE,
-                                    (efi_physical_addr_t *)reserve_addr);
+               status = EFI_OUT_OF_RESOURCES;
        }
 
        if (status != EFI_SUCCESS) {
-               *reserve_size = kernel_memsize + TEXT_OFFSET;
-               status = efi_low_alloc(*reserve_size,
-                                      MIN_KIMG_ALIGN, reserve_addr);
+               if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align)) {
+                       /*
+                        * Just execute from wherever we were loaded by the
+                        * UEFI PE/COFF loader if the alignment is suitable.
+                        */
+                       *image_addr = (u64)_text;
+                       *reserve_size = 0;
+                       return EFI_SUCCESS;
+               }
+
+               status = efi_allocate_pages_aligned(*reserve_size, reserve_addr,
+                                                   ULONG_MAX, min_kimg_align);
 
                if (status != EFI_SUCCESS) {
-                       pr_efi_err("Failed to relocate kernel\n");
+                       efi_err("Failed to relocate kernel\n");
                        *reserve_size = 0;
                        return status;
                }
-               *image_addr = *reserve_addr + TEXT_OFFSET;
        }
 
-       if (image->image_base != _text)
-               pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n");
-
+       *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align;
        memcpy((void *)*image_addr, _text, kernel_size);
 
        return EFI_SUCCESS;
index 9f34c72429397de01e65a4f4fd081775b05b883b..89f075275300ee66b8bc55557c56154605e31b47 100644 (file)
  * Copyright 2011 Intel Corporation; author Matt Fleming
  */
 
+#include <stdarg.h>
+
+#include <linux/ctype.h>
 #include <linux/efi.h>
+#include <linux/kernel.h>
+#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
 #include <asm/efi.h>
+#include <asm/setup.h>
 
 #include "efistub.h"
 
-static bool __efistub_global efi_nochunk;
-static bool __efistub_global efi_nokaslr;
-static bool __efistub_global efi_noinitrd;
-static bool __efistub_global efi_quiet;
-static bool __efistub_global efi_novamap;
-static bool __efistub_global efi_nosoftreserve;
-static bool __efistub_global efi_disable_pci_dma =
-                                       IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
+bool efi_nochunk;
+bool efi_nokaslr;
+bool efi_noinitrd;
+int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
+bool efi_novamap;
 
-bool __pure nochunk(void)
-{
-       return efi_nochunk;
-}
-bool __pure nokaslr(void)
-{
-       return efi_nokaslr;
-}
-bool __pure noinitrd(void)
+static bool efi_nosoftreserve;
+static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
+
+bool __pure __efi_soft_reserve_enabled(void)
 {
-       return efi_noinitrd;
+       return !efi_nosoftreserve;
 }
-bool __pure is_quiet(void)
+
+void efi_char16_puts(efi_char16_t *str)
 {
-       return efi_quiet;
+       efi_call_proto(efi_table_attr(efi_system_table, con_out),
+                      output_string, str);
 }
-bool __pure novamap(void)
+
+static
+u32 utf8_to_utf32(const u8 **s8)
 {
-       return efi_novamap;
+       u32 c32;
+       u8 c0, cx;
+       size_t clen, i;
+
+       c0 = cx = *(*s8)++;
+       /*
+        * The position of the most-significant 0 bit gives us the length of
+        * a multi-octet encoding.
+        */
+       for (clen = 0; cx & 0x80; ++clen)
+               cx <<= 1;
+       /*
+        * If the 0 bit is in position 8, this is a valid single-octet
+        * encoding. If the 0 bit is in position 7 or positions 1-3, the
+        * encoding is invalid.
+        * In either case, we just return the first octet.
+        */
+       if (clen < 2 || clen > 4)
+               return c0;
+       /* Get the bits from the first octet. */
+       c32 = cx >> clen--;
+       for (i = 0; i < clen; ++i) {
+               /* Trailing octets must have 10 in most significant bits. */
+               cx = (*s8)[i] ^ 0x80;
+               if (cx & 0xc0)
+                       return c0;
+               c32 = (c32 << 6) | cx;
+       }
+       /*
+        * Check for validity:
+        * - The character must be in the Unicode range.
+        * - It must not be a surrogate.
+        * - It must be encoded using the correct number of octets.
+        */
+       if (c32 > 0x10ffff ||
+           (c32 & 0xf800) == 0xd800 ||
+           clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))
+               return c0;
+       *s8 += clen;
+       return c32;
 }
-bool __pure __efi_soft_reserve_enabled(void)
+
+void efi_puts(const char *str)
 {
-       return !efi_nosoftreserve;
+       efi_char16_t buf[128];
+       size_t pos = 0, lim = ARRAY_SIZE(buf);
+       const u8 *s8 = (const u8 *)str;
+       u32 c32;
+
+       while (*s8) {
+               if (*s8 == '\n')
+                       buf[pos++] = L'\r';
+               c32 = utf8_to_utf32(&s8);
+               if (c32 < 0x10000) {
+                       /* Characters in plane 0 use a single word. */
+                       buf[pos++] = c32;
+               } else {
+                       /*
+                        * Characters in other planes encode into a surrogate
+                        * pair.
+                        */
+                       buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);
+                       buf[pos++] = 0xdc00 + (c32 & 0x3ff);
+               }
+               if (*s8 == '\0' || pos >= lim - 2) {
+                       buf[pos] = L'\0';
+                       efi_char16_puts(buf);
+                       pos = 0;
+               }
+       }
 }
 
-void efi_printk(char *str)
+int efi_printk(const char *fmt, ...)
 {
-       char *s8;
+       char printf_buf[256];
+       va_list args;
+       int printed;
+       int loglevel = printk_get_level(fmt);
+
+       switch (loglevel) {
+       case '0' ... '9':
+               loglevel -= '0';
+               break;
+       default:
+               /*
+                * Use loglevel -1 for cases where we just want to print to
+                * the screen.
+                */
+               loglevel = -1;
+               break;
+       }
 
-       for (s8 = str; *s8; s8++) {
-               efi_char16_t ch[2] = { 0 };
+       if (loglevel >= efi_loglevel)
+               return 0;
 
-               ch[0] = *s8;
-               if (*s8 == '\n') {
-                       efi_char16_t nl[2] = { '\r', 0 };
-                       efi_char16_printk(nl);
-               }
+       if (loglevel >= 0)
+               efi_puts("EFI stub: ");
+
+       fmt = printk_skip_level(fmt);
+
+       va_start(args, fmt);
+       printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);
+       va_end(args);
 
-               efi_char16_printk(ch);
+       efi_puts(printf_buf);
+       if (printed >= sizeof(printf_buf)) {
+               efi_puts("[Message truncated]\n");
+               return -1;
        }
+
+       return printed;
 }
 
 /*
@@ -91,7 +182,7 @@ efi_status_t efi_parse_options(char const *cmdline)
                if (!strcmp(param, "nokaslr")) {
                        efi_nokaslr = true;
                } else if (!strcmp(param, "quiet")) {
-                       efi_quiet = true;
+                       efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
                } else if (!strcmp(param, "noinitrd")) {
                        efi_noinitrd = true;
                } else if (!strcmp(param, "efi") && val) {
@@ -105,104 +196,91 @@ efi_status_t efi_parse_options(char const *cmdline)
                                efi_disable_pci_dma = true;
                        if (parse_option_str(val, "no_disable_early_pci_dma"))
                                efi_disable_pci_dma = false;
+                       if (parse_option_str(val, "debug"))
+                               efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
+               } else if (!strcmp(param, "video") &&
+                          val && strstarts(val, "efifb:")) {
+                       efi_parse_option_graphics(val + strlen("efifb:"));
                }
        }
        efi_bs_call(free_pool, buf);
        return EFI_SUCCESS;
 }
 
-/*
- * Get the number of UTF-8 bytes corresponding to an UTF-16 character.
- * This overestimates for surrogates, but that is okay.
- */
-static int efi_utf8_bytes(u16 c)
-{
-       return 1 + (c >= 0x80) + (c >= 0x800);
-}
-
-/*
- * Convert an UTF-16 string, not necessarily null terminated, to UTF-8.
- */
-static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
-{
-       unsigned int c;
-
-       while (n--) {
-               c = *src++;
-               if (n && c >= 0xd800 && c <= 0xdbff &&
-                   *src >= 0xdc00 && *src <= 0xdfff) {
-                       c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
-                       src++;
-                       n--;
-               }
-               if (c >= 0xd800 && c <= 0xdfff)
-                       c = 0xfffd; /* Unmatched surrogate */
-               if (c < 0x80) {
-                       *dst++ = c;
-                       continue;
-               }
-               if (c < 0x800) {
-                       *dst++ = 0xc0 + (c >> 6);
-                       goto t1;
-               }
-               if (c < 0x10000) {
-                       *dst++ = 0xe0 + (c >> 12);
-                       goto t2;
-               }
-               *dst++ = 0xf0 + (c >> 18);
-               *dst++ = 0x80 + ((c >> 12) & 0x3f);
-       t2:
-               *dst++ = 0x80 + ((c >> 6) & 0x3f);
-       t1:
-               *dst++ = 0x80 + (c & 0x3f);
-       }
-
-       return dst;
-}
-
 /*
  * Convert the unicode UEFI command line to ASCII to pass to kernel.
  * Size of memory allocated return in *cmd_line_len.
  * Returns NULL on error.
  */
-char *efi_convert_cmdline(efi_loaded_image_t *image,
-                         int *cmd_line_len, unsigned long max_addr)
+char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
 {
        const u16 *s2;
-       u8 *s1 = NULL;
        unsigned long cmdline_addr = 0;
-       int load_options_chars = efi_table_attr(image, load_options_size) / 2;
+       int options_chars = efi_table_attr(image, load_options_size) / 2;
        const u16 *options = efi_table_attr(image, load_options);
-       int options_bytes = 0;  /* UTF-8 bytes */
-       int options_chars = 0;  /* UTF-16 chars */
+       int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
+       bool in_quote = false;
        efi_status_t status;
-       u16 zero = 0;
 
        if (options) {
                s2 = options;
-               while (*s2 && *s2 != '\n'
-                      && options_chars < load_options_chars) {
-                       options_bytes += efi_utf8_bytes(*s2++);
-                       options_chars++;
+               while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
+                       u16 c = *s2++;
+
+                       if (c < 0x80) {
+                               if (c == L'\0' || c == L'\n')
+                                       break;
+                               if (c == L'"')
+                                       in_quote = !in_quote;
+                               else if (!in_quote && isspace((char)c))
+                                       safe_options_bytes = options_bytes;
+
+                               options_bytes++;
+                               continue;
+                       }
+
+                       /*
+                        * Get the number of UTF-8 bytes corresponding to a
+                        * UTF-16 character.
+                        * The first part handles everything in the BMP.
+                        */
+                       options_bytes += 2 + (c >= 0x800);
+                       /*
+                        * Add one more byte for valid surrogate pairs. Invalid
+                        * surrogates will be replaced with 0xfffd and take up
+                        * only 3 bytes.
+                        */
+                       if ((c & 0xfc00) == 0xd800) {
+                               /*
+                                * If the very last word is a high surrogate,
+                                * we must ignore it since we can't access the
+                                * low surrogate.
+                                */
+                               if (!options_chars) {
+                                       options_bytes -= 3;
+                               } else if ((*s2 & 0xfc00) == 0xdc00) {
+                                       options_bytes++;
+                                       options_chars--;
+                                       s2++;
+                               }
+                       }
+               }
+               if (options_bytes >= COMMAND_LINE_SIZE) {
+                       options_bytes = safe_options_bytes;
+                       efi_err("Command line is too long: truncated to %d bytes\n",
+                               options_bytes);
                }
-       }
-
-       if (!options_chars) {
-               /* No command line options, so return empty string*/
-               options = &zero;
        }
 
        options_bytes++;        /* NUL termination */
 
-       status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr);
+       status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes,
+                            (void **)&cmdline_addr);
        if (status != EFI_SUCCESS)
                return NULL;
 
-       s1 = (u8 *)cmdline_addr;
-       s2 = (const u16 *)options;
-
-       s1 = efi_utf16_to_utf8(s1, s2, options_chars);
-       *s1 = '\0';
+       snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
+                options_bytes - 1, options);
 
        *cmd_line_len = options_bytes;
        return (char *)cmdline_addr;
@@ -285,8 +363,8 @@ fail:
 
 void *get_efi_config_table(efi_guid_t guid)
 {
-       unsigned long tables = efi_table_attr(efi_system_table(), tables);
-       int nr_tables = efi_table_attr(efi_system_table(), nr_tables);
+       unsigned long tables = efi_table_attr(efi_system_table, tables);
+       int nr_tables = efi_table_attr(efi_system_table, nr_tables);
        int i;
 
        for (i = 0; i < nr_tables; i++) {
@@ -301,12 +379,6 @@ void *get_efi_config_table(efi_guid_t guid)
        return NULL;
 }
 
-void efi_char16_printk(efi_char16_t *str)
-{
-       efi_call_proto(efi_table_attr(efi_system_table(), con_out),
-                      output_string, str);
-}
-
 /*
  * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way
  * for the firmware or bootloader to expose the initrd data directly to the stub
@@ -348,6 +420,7 @@ static const struct {
  *             %EFI_OUT_OF_RESOURCES if memory allocation failed
  *             %EFI_LOAD_ERROR in all other cases
  */
+static
 efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
                                      unsigned long *load_size,
                                      unsigned long max)
@@ -360,9 +433,6 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
        efi_handle_t handle;
        efi_status_t status;
 
-       if (!load_addr || !load_size)
-               return EFI_INVALID_PARAMETER;
-
        dp = (efi_device_path_protocol_t *)&initrd_dev_path;
        status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
        if (status != EFI_SUCCESS)
@@ -392,3 +462,80 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
        *load_size = initrd_size;
        return EFI_SUCCESS;
 }
+
+static
+efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image,
+                                    unsigned long *load_addr,
+                                    unsigned long *load_size,
+                                    unsigned long soft_limit,
+                                    unsigned long hard_limit)
+{
+       if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) ||
+           (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) {
+               *load_addr = *load_size = 0;
+               return EFI_SUCCESS;
+       }
+
+       return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
+                                   soft_limit, hard_limit,
+                                   load_addr, load_size);
+}
+
+efi_status_t efi_load_initrd(efi_loaded_image_t *image,
+                            unsigned long *load_addr,
+                            unsigned long *load_size,
+                            unsigned long soft_limit,
+                            unsigned long hard_limit)
+{
+       efi_status_t status;
+
+       if (!load_addr || !load_size)
+               return EFI_INVALID_PARAMETER;
+
+       status = efi_load_initrd_dev_path(load_addr, load_size, hard_limit);
+       if (status == EFI_SUCCESS) {
+               efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
+       } else if (status == EFI_NOT_FOUND) {
+               status = efi_load_initrd_cmdline(image, load_addr, load_size,
+                                                soft_limit, hard_limit);
+               if (status == EFI_SUCCESS && *load_size > 0)
+                       efi_info("Loaded initrd from command line option\n");
+       }
+
+       return status;
+}
+
+efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key)
+{
+       efi_event_t events[2], timer;
+       unsigned long index;
+       efi_simple_text_input_protocol_t *con_in;
+       efi_status_t status;
+
+       con_in = efi_table_attr(efi_system_table, con_in);
+       if (!con_in)
+               return EFI_UNSUPPORTED;
+       efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key));
+
+       status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       status = efi_bs_call(set_timer, timer, EfiTimerRelative,
+                            EFI_100NSEC_PER_USEC * usec);
+       if (status != EFI_SUCCESS)
+               return status;
+       efi_set_event_at(events, 1, timer);
+
+       status = efi_bs_call(wait_for_event, 2, events, &index);
+       if (status == EFI_SUCCESS) {
+               if (index == 0)
+                       status = efi_call_proto(con_in, read_keystroke, key);
+               else
+                       status = EFI_TIMEOUT;
+       }
+
+       efi_bs_call(close_event, timer);
+
+       return status;
+}
similarity index 83%
rename from drivers/firmware/efi/libstub/arm-stub.c
rename to drivers/firmware/efi/libstub/efi-stub.c
index 48161b1dd098f2694f8d5baf02403b6291928ea8..e97370bdfdb0ac87cfbe34b1a2c4e1d14956bcf8 100644 (file)
 #endif
 
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
-static bool __efistub_global flat_va_mapping;
+static bool flat_va_mapping;
 
-static efi_system_table_t *__efistub_global sys_table;
-
-__pure efi_system_table_t *efi_system_table(void)
-{
-       return sys_table;
-}
+const efi_system_table_t *efi_system_table;
 
 static struct screen_info *setup_graphics(void)
 {
@@ -69,7 +64,7 @@ static struct screen_info *setup_graphics(void)
        return si;
 }
 
-void install_memreserve_table(void)
+static void install_memreserve_table(void)
 {
        struct linux_efi_memreserve *rsv;
        efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID;
@@ -78,7 +73,7 @@ void install_memreserve_table(void)
        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
                             (void **)&rsv);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to allocate memreserve entry!\n");
+               efi_err("Failed to allocate memreserve entry!\n");
                return;
        }
 
@@ -89,7 +84,7 @@ void install_memreserve_table(void)
        status = efi_bs_call(install_configuration_table,
                             &memreserve_table_guid, rsv);
        if (status != EFI_SUCCESS)
-               pr_efi_err("Failed to install memreserve config table!\n");
+               efi_err("Failed to install memreserve config table!\n");
 }
 
 static unsigned long get_dram_base(void)
@@ -149,7 +144,8 @@ asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
  * for both archictectures, with the arch-specific code provided in the
  * handle_kernel_image() function.
  */
-efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
+efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
+                                  efi_system_table_t *sys_table_arg)
 {
        efi_loaded_image_t *image;
        efi_status_t status;
@@ -171,10 +167,10 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
        efi_properties_table_t *prop_tbl;
        unsigned long max_addr;
 
-       sys_table = sys_table_arg;
+       efi_system_table = sys_table_arg;
 
        /* Check if we were booted by the EFI firmware */
-       if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+       if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
                status = EFI_INVALID_PARAMETER;
                goto fail;
        }
@@ -188,16 +184,16 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
         * information about the running image, such as size and the command
         * line.
         */
-       status = sys_table->boottime->handle_protocol(handle,
+       status = efi_system_table->boottime->handle_protocol(handle,
                                        &loaded_image_proto, (void *)&image);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to get loaded image protocol\n");
+               efi_err("Failed to get loaded image protocol\n");
                goto fail;
        }
 
        dram_base = get_dram_base();
        if (dram_base == EFI_ERROR) {
-               pr_efi_err("Failed to find DRAM base\n");
+               efi_err("Failed to find DRAM base\n");
                status = EFI_LOAD_ERROR;
                goto fail;
        }
@@ -207,22 +203,32 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
         * protocol. We are going to copy the command line into the
         * device tree, so this can be allocated anywhere.
         */
-       cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX);
+       cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
        if (!cmdline_ptr) {
-               pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
+               efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
                status = EFI_OUT_OF_RESOURCES;
                goto fail;
        }
 
        if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
            IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
-           cmdline_size == 0)
-               efi_parse_options(CONFIG_CMDLINE);
+           cmdline_size == 0) {
+               status = efi_parse_options(CONFIG_CMDLINE);
+               if (status != EFI_SUCCESS) {
+                       efi_err("Failed to parse options\n");
+                       goto fail_free_cmdline;
+               }
+       }
 
-       if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0)
-               efi_parse_options(cmdline_ptr);
+       if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) {
+               status = efi_parse_options(cmdline_ptr);
+               if (status != EFI_SUCCESS) {
+                       efi_err("Failed to parse options\n");
+                       goto fail_free_cmdline;
+               }
+       }
 
-       pr_efi("Booting Linux Kernel...\n");
+       efi_info("Booting Linux Kernel...\n");
 
        si = setup_graphics();
 
@@ -231,8 +237,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
                                     &reserve_size,
                                     dram_base, image);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to relocate kernel\n");
-               goto fail_free_cmdline;
+               efi_err("Failed to relocate kernel\n");
+               goto fail_free_screeninfo;
        }
 
        efi_retrieve_tpm2_eventlog();
@@ -250,42 +256,34 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
        if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) ||
             secure_boot != efi_secureboot_mode_disabled) {
                if (strstr(cmdline_ptr, "dtb="))
-                       pr_efi("Ignoring DTB from command line.\n");
+                       efi_err("Ignoring DTB from command line.\n");
        } else {
                status = efi_load_dtb(image, &fdt_addr, &fdt_size);
 
                if (status != EFI_SUCCESS) {
-                       pr_efi_err("Failed to load device tree!\n");
+                       efi_err("Failed to load device tree!\n");
                        goto fail_free_image;
                }
        }
 
        if (fdt_addr) {
-               pr_efi("Using DTB from command line\n");
+               efi_info("Using DTB from command line\n");
        } else {
                /* Look for a device tree configuration table entry. */
                fdt_addr = (uintptr_t)get_fdt(&fdt_size);
                if (fdt_addr)
-                       pr_efi("Using DTB from configuration table\n");
+                       efi_info("Using DTB from configuration table\n");
        }
 
        if (!fdt_addr)
-               pr_efi("Generating empty DTB\n");
+               efi_info("Generating empty DTB\n");
 
-       if (!noinitrd()) {
+       if (!efi_noinitrd) {
                max_addr = efi_get_max_initrd_addr(dram_base, image_addr);
-               status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size,
-                                                 max_addr);
-               if (status == EFI_SUCCESS) {
-                       pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
-               } else if (status == EFI_NOT_FOUND) {
-                       status = efi_load_initrd(image, &initrd_addr, &initrd_size,
-                                                ULONG_MAX, max_addr);
-                       if (status == EFI_SUCCESS && initrd_size > 0)
-                               pr_efi("Loaded initrd from command line option\n");
-               }
+               status = efi_load_initrd(image, &initrd_addr, &initrd_size,
+                                        ULONG_MAX, max_addr);
                if (status != EFI_SUCCESS)
-                       pr_efi_err("Failed to load initrd!\n");
+                       efi_err("Failed to load initrd!\n");
        }
 
        efi_random_get_seed();
@@ -303,7 +301,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
                           EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
 
        /* hibernation expects the runtime regions to stay in the same place */
-       if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) {
+       if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) {
                /*
                 * Randomize the base of the UEFI runtime services region.
                 * Preserve the 2 MB alignment of the region by taking a
@@ -335,7 +333,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg)
        /* not reached */
 
 fail_free_initrd:
-       pr_efi_err("Failed to update FDT and exit boot services\n");
+       efi_err("Failed to update FDT and exit boot services\n");
 
        efi_free(initrd_size, initrd_addr);
        efi_free(fdt_size, fdt_addr);
@@ -343,9 +341,10 @@ fail_free_initrd:
 fail_free_image:
        efi_free(image_size, image_addr);
        efi_free(reserve_size, reserve_addr);
-fail_free_cmdline:
+fail_free_screeninfo:
        free_screen_info(si);
-       efi_free(cmdline_size, (unsigned long)cmdline_ptr);
+fail_free_cmdline:
+       efi_bs_call(free_pool, cmdline_ptr);
 fail:
        return status;
 }
@@ -376,7 +375,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
                size = in->num_pages * EFI_PAGE_SIZE;
 
                in->virt_addr = in->phys_addr;
-               if (novamap()) {
+               if (efi_novamap) {
                        continue;
                }
 
index 62943992f02fe5d30a5c7a36ad83b9667bf1b068..bcd8c0a785f0e73a220179f3b9ee4334e77c2308 100644 (file)
@@ -3,6 +3,13 @@
 #ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H
 #define _DRIVERS_FIRMWARE_EFI_EFISTUB_H
 
+#include <linux/compiler.h>
+#include <linux/efi.h>
+#include <linux/kernel.h>
+#include <linux/kern_levels.h>
+#include <linux/types.h>
+#include <asm/efi.h>
+
 /* error code which can't be mistaken for valid address */
 #define EFI_ERROR      (~0UL)
 
 #define EFI_ALLOC_ALIGN                EFI_PAGE_SIZE
 #endif
 
-#if defined(CONFIG_ARM) || defined(CONFIG_X86)
-#define __efistub_global       __section(.data)
-#else
-#define __efistub_global
-#endif
+extern bool efi_nochunk;
+extern bool efi_nokaslr;
+extern bool efi_noinitrd;
+extern int efi_loglevel;
+extern bool efi_novamap;
 
-extern bool __pure nochunk(void);
-extern bool __pure nokaslr(void);
-extern bool __pure noinitrd(void);
-extern bool __pure is_quiet(void);
-extern bool __pure novamap(void);
+extern const efi_system_table_t *efi_system_table;
 
-extern __pure efi_system_table_t  *efi_system_table(void);
+efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
+                                  efi_system_table_t *sys_table_arg);
 
-#define pr_efi(msg)            do {                    \
-       if (!is_quiet()) efi_printk("EFI stub: "msg);   \
-} while (0)
+#ifndef ARCH_HAS_EFISTUB_WRAPPERS
 
-#define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg)
+#define efi_is_native()                (true)
+#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
+#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
+#define efi_table_attr(inst, attr)     (inst->attr)
+#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
+
+#endif
+
+#define efi_info(fmt, ...) \
+       efi_printk(KERN_INFO fmt, ##__VA_ARGS__)
+#define efi_err(fmt, ...) \
+       efi_printk(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__)
+#define efi_debug(fmt, ...) \
+       efi_printk(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__)
 
 /* Helper macros for the usual case of using simple C variables: */
 #ifndef fdt_setprop_inplace_var
@@ -77,6 +92,13 @@ extern __pure efi_system_table_t  *efi_system_table(void);
                ((handle = efi_get_handle_at((array), i)) || true);     \
             i++)
 
+static inline
+void efi_set_u64_split(u64 data, u32 *lo, u32 *hi)
+{
+       *lo = lower_32_bits(data);
+       *hi = upper_32_bits(data);
+}
+
 /*
  * Allocation types for calls to boottime->allocate_pages.
  */
@@ -92,6 +114,16 @@ extern __pure efi_system_table_t  *efi_system_table(void);
 #define EFI_LOCATE_BY_REGISTER_NOTIFY          1
 #define EFI_LOCATE_BY_PROTOCOL                 2
 
+/*
+ * boottime->stall takes the time period in microseconds
+ */
+#define EFI_USEC_PER_SEC               1000000
+
+/*
+ * boottime->set_timer takes the time in 100ns units
+ */
+#define EFI_100NSEC_PER_USEC   ((u64)10)
+
 /*
  * An efi_boot_memmap is used by efi_get_memory_map() to return the
  * EFI memory map in a dynamically allocated buffer.
@@ -116,6 +148,39 @@ struct efi_boot_memmap {
 
 typedef struct efi_generic_dev_path efi_device_path_protocol_t;
 
+typedef void *efi_event_t;
+/* Note that notifications won't work in mixed mode */
+typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *);
+
+#define EFI_EVT_TIMER          0x80000000U
+#define EFI_EVT_RUNTIME                0x40000000U
+#define EFI_EVT_NOTIFY_WAIT    0x00000100U
+#define EFI_EVT_NOTIFY_SIGNAL  0x00000200U
+
+/*
+ * boottime->wait_for_event takes an array of events as input.
+ * Provide a helper to set it up correctly for mixed mode.
+ */
+static inline
+void efi_set_event_at(efi_event_t *events, size_t idx, efi_event_t event)
+{
+       if (efi_is_native())
+               events[idx] = event;
+       else
+               ((u32 *)events)[idx] = (u32)(unsigned long)event;
+}
+
+#define EFI_TPL_APPLICATION    4
+#define EFI_TPL_CALLBACK       8
+#define EFI_TPL_NOTIFY         16
+#define EFI_TPL_HIGH_LEVEL     31
+
+typedef enum {
+       EfiTimerCancel,
+       EfiTimerPeriodic,
+       EfiTimerRelative
+} EFI_TIMER_DELAY;
+
 /*
  * EFI Boot Services table
  */
@@ -134,11 +199,16 @@ union efi_boot_services {
                efi_status_t (__efiapi *allocate_pool)(int, unsigned long,
                                                       void **);
                efi_status_t (__efiapi *free_pool)(void *);
-               void *create_event;
-               void *set_timer;
-               void *wait_for_event;
+               efi_status_t (__efiapi *create_event)(u32, unsigned long,
+                                                     efi_event_notify_t, void *,
+                                                     efi_event_t *);
+               efi_status_t (__efiapi *set_timer)(efi_event_t,
+                                                 EFI_TIMER_DELAY, u64);
+               efi_status_t (__efiapi *wait_for_event)(unsigned long,
+                                                       efi_event_t *,
+                                                       unsigned long *);
                void *signal_event;
-               void *close_event;
+               efi_status_t (__efiapi *close_event)(efi_event_t);
                void *check_event;
                void *install_protocol_interface;
                void *reinstall_protocol_interface;
@@ -165,7 +235,7 @@ union efi_boot_services {
                efi_status_t (__efiapi *exit_boot_services)(efi_handle_t,
                                                            unsigned long);
                void *get_next_monotonic_count;
-               void *stall;
+               efi_status_t (__efiapi *stall)(unsigned long);
                void *set_watchdog_timer;
                void *connect_controller;
                efi_status_t (__efiapi *disconnect_controller)(efi_handle_t,
@@ -250,6 +320,27 @@ union efi_uga_draw_protocol {
        } mixed_mode;
 };
 
+typedef struct {
+       u16 scan_code;
+       efi_char16_t unicode_char;
+} efi_input_key_t;
+
+union efi_simple_text_input_protocol {
+       struct {
+               void *reset;
+               efi_status_t (__efiapi *read_keystroke)(efi_simple_text_input_protocol_t *,
+                                                       efi_input_key_t *);
+               efi_event_t wait_for_key;
+       };
+       struct {
+               u32 reset;
+               u32 read_keystroke;
+               u32 wait_for_key;
+       } mixed_mode;
+};
+
+efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key);
+
 union efi_simple_text_output_protocol {
        struct {
                void *reset;
@@ -311,8 +402,10 @@ typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t;
 
 union efi_graphics_output_protocol {
        struct {
-               void *query_mode;
-               void *set_mode;
+               efi_status_t (__efiapi *query_mode)(efi_graphics_output_protocol_t *,
+                                                   u32, unsigned long *,
+                                                   efi_graphics_output_mode_info_t **);
+               efi_status_t (__efiapi *set_mode)  (efi_graphics_output_protocol_t *, u32);
                void *blt;
                efi_graphics_output_protocol_mode_t *mode;
        };
@@ -600,8 +693,6 @@ efi_status_t efi_exit_boot_services(void *handle,
                                    void *priv,
                                    efi_exit_boot_map_processing priv_func);
 
-void efi_char16_printk(efi_char16_t *);
-
 efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
                                            unsigned long *new_fdt_addr,
                                            unsigned long max_addr,
@@ -625,33 +716,24 @@ efi_status_t check_platform_features(void);
 
 void *get_efi_config_table(efi_guid_t guid);
 
-void efi_printk(char *str);
+/* NOTE: These functions do not print a trailing newline after the string */
+void efi_char16_puts(efi_char16_t *);
+void efi_puts(const char *str);
+
+__printf(1, 2) int efi_printk(char const *fmt, ...);
 
 void efi_free(unsigned long size, unsigned long addr);
 
-char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len,
-                         unsigned long max_addr);
+char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len);
 
 efi_status_t efi_get_memory_map(struct efi_boot_memmap *map);
 
-efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
-                                unsigned long *addr, unsigned long min);
-
-static inline
-efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
-                          unsigned long *addr)
-{
-       /*
-        * Don't allocate at 0x0. It will confuse code that
-        * checks pointers against NULL. Skip the first 8
-        * bytes so we start at a nice even number.
-        */
-       return efi_low_alloc_above(size, align, addr, 0x8);
-}
-
 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
                                unsigned long max);
 
+efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr,
+                                       unsigned long max, unsigned long align);
+
 efi_status_t efi_relocate_kernel(unsigned long *image_addr,
                                 unsigned long image_size,
                                 unsigned long alloc_size,
@@ -661,12 +743,27 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr,
 
 efi_status_t efi_parse_options(char const *cmdline);
 
+void efi_parse_option_graphics(char *option);
+
 efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
                           unsigned long size);
 
-efi_status_t efi_load_dtb(efi_loaded_image_t *image,
-                         unsigned long *load_addr,
-                         unsigned long *load_size);
+efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
+                                 const efi_char16_t *optstr,
+                                 int optstr_size,
+                                 unsigned long soft_limit,
+                                 unsigned long hard_limit,
+                                 unsigned long *load_addr,
+                                 unsigned long *load_size);
+
+
+static inline efi_status_t efi_load_dtb(efi_loaded_image_t *image,
+                                       unsigned long *load_addr,
+                                       unsigned long *load_size)
+{
+       return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2,
+                                   ULONG_MAX, ULONG_MAX, load_addr, load_size);
+}
 
 efi_status_t efi_load_initrd(efi_loaded_image_t *image,
                             unsigned long *load_addr,
@@ -674,8 +771,4 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
                             unsigned long soft_limit,
                             unsigned long hard_limit);
 
-efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr,
-                                     unsigned long *load_size,
-                                     unsigned long max);
-
 #endif
index 46cffac7a5f1f0093ea68f4066eda03c7a9701ad..11ecf3c4640ebce49d89a4349ffa05621e1f0d6d 100644 (file)
@@ -39,7 +39,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
        /* Do some checks on provided FDT, if it exists: */
        if (orig_fdt) {
                if (fdt_check_header(orig_fdt)) {
-                       pr_efi_err("Device Tree header not valid!\n");
+                       efi_err("Device Tree header not valid!\n");
                        return EFI_LOAD_ERROR;
                }
                /*
@@ -47,7 +47,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
                 * configuration table:
                 */
                if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
-                       pr_efi_err("Truncated device tree! foo!\n");
+                       efi_err("Truncated device tree! foo!\n");
                        return EFI_LOAD_ERROR;
                }
        }
@@ -110,7 +110,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
 
        /* Add FDT entries for EFI runtime services in chosen node. */
        node = fdt_subnode_offset(fdt, 0, "chosen");
-       fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table());
+       fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table);
 
        status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64);
        if (status)
@@ -270,16 +270,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
         */
        status = efi_get_memory_map(&map);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Unable to retrieve UEFI memory map.\n");
+               efi_err("Unable to retrieve UEFI memory map.\n");
                return status;
        }
 
-       pr_efi("Exiting boot services and installing virtual address map...\n");
+       efi_info("Exiting boot services and installing virtual address map...\n");
 
        map.map = &memory_map;
        status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Unable to allocate memory for new device tree.\n");
+               efi_err("Unable to allocate memory for new device tree.\n");
                goto fail;
        }
 
@@ -296,7 +296,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
                            initrd_addr, initrd_size);
 
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Unable to construct new device tree.\n");
+               efi_err("Unable to construct new device tree.\n");
                goto fail_free_new_fdt;
        }
 
@@ -310,11 +310,11 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
        if (status == EFI_SUCCESS) {
                efi_set_virtual_address_map_t *svam;
 
-               if (novamap())
+               if (efi_novamap)
                        return EFI_SUCCESS;
 
                /* Install the new virtual address map */
-               svam = efi_system_table()->runtime->set_virtual_address_map;
+               svam = efi_system_table->runtime->set_virtual_address_map;
                status = svam(runtime_entry_count * desc_size, desc_size,
                              desc_ver, runtime_map);
 
@@ -342,13 +342,13 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
                return EFI_SUCCESS;
        }
 
-       pr_efi_err("Exit boot services failed.\n");
+       efi_err("Exit boot services failed.\n");
 
 fail_free_new_fdt:
        efi_free(MAX_FDT_SIZE, *new_fdt_addr);
 
 fail:
-       efi_system_table()->boottime->free_pool(runtime_map);
+       efi_system_table->boottime->free_pool(runtime_map);
 
        return EFI_LOAD_ERROR;
 }
@@ -363,7 +363,7 @@ void *get_fdt(unsigned long *fdt_size)
                return NULL;
 
        if (fdt_check_header(fdt) != 0) {
-               pr_efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
+               efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
                return NULL;
        }
        *fdt_size = fdt_totalsize(fdt);
index ea66b1f16a79d17b4b5bf1cce7a92042a296af69..2005e33b33d57fecf57d6533dfd5588dc7172575 100644 (file)
@@ -46,16 +46,14 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume,
 
        status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to open file: ");
-               efi_char16_printk(fi->filename);
-               efi_printk("\n");
+               efi_err("Failed to open file: %ls\n", fi->filename);
                return status;
        }
 
        info_sz = sizeof(struct finfo);
        status = fh->get_info(fh, &info_guid, &info_sz, fi);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to get file info\n");
+               efi_err("Failed to get file info\n");
                fh->close(fh);
                return status;
        }
@@ -75,13 +73,13 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image,
        status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
                             (void **)&io);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to handle fs_proto\n");
+               efi_err("Failed to handle fs_proto\n");
                return status;
        }
 
        status = io->open_volume(io, fh);
        if (status != EFI_SUCCESS)
-               pr_efi_err("Failed to open volume\n");
+               efi_err("Failed to open volume\n");
 
        return status;
 }
@@ -121,13 +119,13 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len,
  * We only support loading a file from the same filesystem as
  * the kernel image.
  */
-static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
-                                        const efi_char16_t *optstr,
-                                        int optstr_size,
-                                        unsigned long soft_limit,
-                                        unsigned long hard_limit,
-                                        unsigned long *load_addr,
-                                        unsigned long *load_size)
+efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
+                                 const efi_char16_t *optstr,
+                                 int optstr_size,
+                                 unsigned long soft_limit,
+                                 unsigned long hard_limit,
+                                 unsigned long *load_addr,
+                                 unsigned long *load_size)
 {
        const efi_char16_t *cmdline = image->load_options;
        int cmdline_len = image->load_options_size / 2;
@@ -142,7 +140,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
        if (!load_addr || !load_size)
                return EFI_INVALID_PARAMETER;
 
-       if (IS_ENABLED(CONFIG_X86) && !nochunk())
+       if (IS_ENABLED(CONFIG_X86) && !efi_nochunk)
                efi_chunk_size = EFI_READ_CHUNK_SIZE;
 
        alloc_addr = alloc_size = 0;
@@ -191,7 +189,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
                                                            &alloc_addr,
                                                            hard_limit);
                        if (status != EFI_SUCCESS) {
-                               pr_efi_err("Failed to allocate memory for files\n");
+                               efi_err("Failed to allocate memory for files\n");
                                goto err_close_file;
                        }
 
@@ -215,7 +213,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
 
                        status = file->read(file, &chunksize, addr);
                        if (status != EFI_SUCCESS) {
-                               pr_efi_err("Failed to read file\n");
+                               efi_err("Failed to read file\n");
                                goto err_close_file;
                        }
                        addr += chunksize;
@@ -239,21 +237,3 @@ err_close_volume:
        efi_free(alloc_size, alloc_addr);
        return status;
 }
-
-efi_status_t efi_load_dtb(efi_loaded_image_t *image,
-                         unsigned long *load_addr,
-                         unsigned long *load_size)
-{
-       return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2,
-                                   ULONG_MAX, ULONG_MAX, load_addr, load_size);
-}
-
-efi_status_t efi_load_initrd(efi_loaded_image_t *image,
-                            unsigned long *load_addr,
-                            unsigned long *load_size,
-                            unsigned long soft_limit,
-                            unsigned long hard_limit)
-{
-       return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
-                                   soft_limit, hard_limit, load_addr, load_size);
-}
index 55e6b3f286fe0bc9e012062e316e8a404d1f2d92..ea5da307d542fc2a3dc991f04ccfed4f542c2a52 100644 (file)
  *
  * ----------------------------------------------------------------------- */
 
+#include <linux/bitops.h>
+#include <linux/ctype.h>
 #include <linux/efi.h>
 #include <linux/screen_info.h>
+#include <linux/string.h>
 #include <asm/efi.h>
 #include <asm/setup.h>
 
 #include "efistub.h"
 
-static void find_bits(unsigned long mask, u8 *pos, u8 *size)
+enum efi_cmdline_option {
+       EFI_CMDLINE_NONE,
+       EFI_CMDLINE_MODE_NUM,
+       EFI_CMDLINE_RES,
+       EFI_CMDLINE_AUTO,
+       EFI_CMDLINE_LIST
+};
+
+static struct {
+       enum efi_cmdline_option option;
+       union {
+               u32 mode;
+               struct {
+                       u32 width, height;
+                       int format;
+                       u8 depth;
+               } res;
+       };
+} cmdline = { .option = EFI_CMDLINE_NONE };
+
+static bool parse_modenum(char *option, char **next)
+{
+       u32 m;
+
+       if (!strstarts(option, "mode="))
+               return false;
+       option += strlen("mode=");
+       m = simple_strtoull(option, &option, 0);
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option = EFI_CMDLINE_MODE_NUM;
+       cmdline.mode   = m;
+
+       *next = option;
+       return true;
+}
+
+static bool parse_res(char *option, char **next)
+{
+       u32 w, h, d = 0;
+       int pf = -1;
+
+       if (!isdigit(*option))
+               return false;
+       w = simple_strtoull(option, &option, 10);
+       if (*option++ != 'x' || !isdigit(*option))
+               return false;
+       h = simple_strtoull(option, &option, 10);
+       if (*option == '-') {
+               option++;
+               if (strstarts(option, "rgb")) {
+                       option += strlen("rgb");
+                       pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
+               } else if (strstarts(option, "bgr")) {
+                       option += strlen("bgr");
+                       pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
+               } else if (isdigit(*option))
+                       d = simple_strtoull(option, &option, 10);
+               else
+                       return false;
+       }
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option     = EFI_CMDLINE_RES;
+       cmdline.res.width  = w;
+       cmdline.res.height = h;
+       cmdline.res.format = pf;
+       cmdline.res.depth  = d;
+
+       *next = option;
+       return true;
+}
+
+static bool parse_auto(char *option, char **next)
+{
+       if (!strstarts(option, "auto"))
+               return false;
+       option += strlen("auto");
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option = EFI_CMDLINE_AUTO;
+
+       *next = option;
+       return true;
+}
+
+static bool parse_list(char *option, char **next)
 {
-       u8 first, len;
+       if (!strstarts(option, "list"))
+               return false;
+       option += strlen("list");
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option = EFI_CMDLINE_LIST;
+
+       *next = option;
+       return true;
+}
+
+void efi_parse_option_graphics(char *option)
+{
+       while (*option) {
+               if (parse_modenum(option, &option))
+                       continue;
+               if (parse_res(option, &option))
+                       continue;
+               if (parse_auto(option, &option))
+                       continue;
+               if (parse_list(option, &option))
+                       continue;
+
+               while (*option && *option++ != ',')
+                       ;
+       }
+}
+
+static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
+{
+       efi_status_t status;
+
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+       unsigned long info_size;
+
+       u32 max_mode, cur_mode;
+       int pf;
+
+       mode = efi_table_attr(gop, mode);
+
+       cur_mode = efi_table_attr(mode, mode);
+       if (cmdline.mode == cur_mode)
+               return cur_mode;
+
+       max_mode = efi_table_attr(mode, max_mode);
+       if (cmdline.mode >= max_mode) {
+               efi_err("Requested mode is invalid\n");
+               return cur_mode;
+       }
+
+       status = efi_call_proto(gop, query_mode, cmdline.mode,
+                               &info_size, &info);
+       if (status != EFI_SUCCESS) {
+               efi_err("Couldn't get mode information\n");
+               return cur_mode;
+       }
+
+       pf = info->pixel_format;
+
+       efi_bs_call(free_pool, info);
+
+       if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
+               efi_err("Invalid PixelFormat\n");
+               return cur_mode;
+       }
+
+       return cmdline.mode;
+}
+
+static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
+{
+       if (pixel_format == PIXEL_BIT_MASK) {
+               u32 mask = pixel_info.red_mask | pixel_info.green_mask |
+                          pixel_info.blue_mask | pixel_info.reserved_mask;
+               if (!mask)
+                       return 0;
+               return __fls(mask) - __ffs(mask) + 1;
+       } else
+               return 32;
+}
+
+static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
+{
+       efi_status_t status;
+
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+       unsigned long info_size;
+
+       u32 max_mode, cur_mode;
+       int pf;
+       efi_pixel_bitmask_t pi;
+       u32 m, w, h;
+
+       mode = efi_table_attr(gop, mode);
+
+       cur_mode = efi_table_attr(mode, mode);
+       info = efi_table_attr(mode, info);
+       pf = info->pixel_format;
+       pi = info->pixel_information;
+       w  = info->horizontal_resolution;
+       h  = info->vertical_resolution;
+
+       if (w == cmdline.res.width && h == cmdline.res.height &&
+           (cmdline.res.format < 0 || cmdline.res.format == pf) &&
+           (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
+               return cur_mode;
+
+       max_mode = efi_table_attr(mode, max_mode);
+
+       for (m = 0; m < max_mode; m++) {
+               if (m == cur_mode)
+                       continue;
+
+               status = efi_call_proto(gop, query_mode, m,
+                                       &info_size, &info);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               pf = info->pixel_format;
+               pi = info->pixel_information;
+               w  = info->horizontal_resolution;
+               h  = info->vertical_resolution;
+
+               efi_bs_call(free_pool, info);
+
+               if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
+                       continue;
+               if (w == cmdline.res.width && h == cmdline.res.height &&
+                   (cmdline.res.format < 0 || cmdline.res.format == pf) &&
+                   (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
+                       return m;
+       }
+
+       efi_err("Couldn't find requested mode\n");
+
+       return cur_mode;
+}
+
+static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
+{
+       efi_status_t status;
+
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+       unsigned long info_size;
+
+       u32 max_mode, cur_mode, best_mode, area;
+       u8 depth;
+       int pf;
+       efi_pixel_bitmask_t pi;
+       u32 m, w, h, a;
+       u8 d;
+
+       mode = efi_table_attr(gop, mode);
+
+       cur_mode = efi_table_attr(mode, mode);
+       max_mode = efi_table_attr(mode, max_mode);
 
-       first = 0;
-       len = 0;
+       info = efi_table_attr(mode, info);
 
-       if (mask) {
-               while (!(mask & 0x1)) {
-                       mask = mask >> 1;
-                       first++;
+       pf = info->pixel_format;
+       pi = info->pixel_information;
+       w  = info->horizontal_resolution;
+       h  = info->vertical_resolution;
+
+       best_mode = cur_mode;
+       area = w * h;
+       depth = pixel_bpp(pf, pi);
+
+       for (m = 0; m < max_mode; m++) {
+               if (m == cur_mode)
+                       continue;
+
+               status = efi_call_proto(gop, query_mode, m,
+                                       &info_size, &info);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               pf = info->pixel_format;
+               pi = info->pixel_information;
+               w  = info->horizontal_resolution;
+               h  = info->vertical_resolution;
+
+               efi_bs_call(free_pool, info);
+
+               if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
+                       continue;
+               a = w * h;
+               if (a < area)
+                       continue;
+               d = pixel_bpp(pf, pi);
+               if (a > area || d > depth) {
+                       best_mode = m;
+                       area = a;
+                       depth = d;
                }
+       }
+
+       return best_mode;
+}
+
+static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
+{
+       efi_status_t status;
+
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+       unsigned long info_size;
+
+       u32 max_mode, cur_mode;
+       int pf;
+       efi_pixel_bitmask_t pi;
+       u32 m, w, h;
+       u8 d;
+       const char *dstr;
+       bool valid;
+       efi_input_key_t key;
 
-               while (mask & 0x1) {
-                       mask = mask >> 1;
-                       len++;
+       mode = efi_table_attr(gop, mode);
+
+       cur_mode = efi_table_attr(mode, mode);
+       max_mode = efi_table_attr(mode, max_mode);
+
+       efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
+       efi_puts("  * = current mode\n"
+                "  - = unusable mode\n");
+       for (m = 0; m < max_mode; m++) {
+               status = efi_call_proto(gop, query_mode, m,
+                                       &info_size, &info);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               pf = info->pixel_format;
+               pi = info->pixel_information;
+               w  = info->horizontal_resolution;
+               h  = info->vertical_resolution;
+
+               efi_bs_call(free_pool, info);
+
+               valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
+               d = 0;
+               switch (pf) {
+               case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
+                       dstr = "rgb";
+                       break;
+               case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
+                       dstr = "bgr";
+                       break;
+               case PIXEL_BIT_MASK:
+                       dstr = "";
+                       d = pixel_bpp(pf, pi);
+                       break;
+               case PIXEL_BLT_ONLY:
+                       dstr = "blt";
+                       break;
+               default:
+                       dstr = "xxx";
+                       break;
                }
+
+               efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
+                          m,
+                          m == cur_mode ? '*' : ' ',
+                          !valid ? '-' : ' ',
+                          w, h, dstr, d);
+       }
+
+       efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
+       status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
+       if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
+               efi_err("Unable to read key, continuing in 10 seconds\n");
+               efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
+       }
+
+       return cur_mode;
+}
+
+static void set_mode(efi_graphics_output_protocol_t *gop)
+{
+       efi_graphics_output_protocol_mode_t *mode;
+       u32 cur_mode, new_mode;
+
+       switch (cmdline.option) {
+       case EFI_CMDLINE_MODE_NUM:
+               new_mode = choose_mode_modenum(gop);
+               break;
+       case EFI_CMDLINE_RES:
+               new_mode = choose_mode_res(gop);
+               break;
+       case EFI_CMDLINE_AUTO:
+               new_mode = choose_mode_auto(gop);
+               break;
+       case EFI_CMDLINE_LIST:
+               new_mode = choose_mode_list(gop);
+               break;
+       default:
+               return;
+       }
+
+       mode = efi_table_attr(gop, mode);
+       cur_mode = efi_table_attr(mode, mode);
+
+       if (new_mode == cur_mode)
+               return;
+
+       if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
+               efi_err("Failed to set requested mode\n");
+}
+
+static void find_bits(u32 mask, u8 *pos, u8 *size)
+{
+       if (!mask) {
+               *pos = *size = 0;
+               return;
        }
 
-       *pos = first;
-       *size = len;
+       /* UEFI spec guarantees that the set bits are contiguous */
+       *pos  = __ffs(mask);
+       *size = __fls(mask) - *pos + 1;
 }
 
 static void
 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
                 efi_pixel_bitmask_t pixel_info, int pixel_format)
 {
-       if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
-               si->lfb_depth = 32;
-               si->lfb_linelength = pixels_per_scan_line * 4;
-               si->red_size = 8;
-               si->red_pos = 0;
-               si->green_size = 8;
-               si->green_pos = 8;
-               si->blue_size = 8;
-               si->blue_pos = 16;
-               si->rsvd_size = 8;
-               si->rsvd_pos = 24;
-       } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
-               si->lfb_depth = 32;
-               si->lfb_linelength = pixels_per_scan_line * 4;
-               si->red_size = 8;
-               si->red_pos = 16;
-               si->green_size = 8;
-               si->green_pos = 8;
-               si->blue_size = 8;
-               si->blue_pos = 0;
-               si->rsvd_size = 8;
-               si->rsvd_pos = 24;
-       } else if (pixel_format == PIXEL_BIT_MASK) {
-               find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
-               find_bits(pixel_info.green_mask, &si->green_pos,
-                         &si->green_size);
-               find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
-               find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
-                         &si->rsvd_size);
+       if (pixel_format == PIXEL_BIT_MASK) {
+               find_bits(pixel_info.red_mask,
+                         &si->red_pos, &si->red_size);
+               find_bits(pixel_info.green_mask,
+                         &si->green_pos, &si->green_size);
+               find_bits(pixel_info.blue_mask,
+                         &si->blue_pos, &si->blue_size);
+               find_bits(pixel_info.reserved_mask,
+                         &si->rsvd_pos, &si->rsvd_size);
                si->lfb_depth = si->red_size + si->green_size +
                        si->blue_size + si->rsvd_size;
                si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
        } else {
-               si->lfb_depth = 4;
-               si->lfb_linelength = si->lfb_width / 2;
-               si->red_size = 0;
-               si->red_pos = 0;
-               si->green_size = 0;
-               si->green_pos = 0;
-               si->blue_size = 0;
-               si->blue_pos = 0;
-               si->rsvd_size = 0;
-               si->rsvd_pos = 0;
+               if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+                       si->red_pos   = 0;
+                       si->blue_pos  = 16;
+               } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
+                       si->blue_pos  = 0;
+                       si->red_pos   = 16;
+               }
+
+               si->green_pos = 8;
+               si->rsvd_pos  = 24;
+               si->red_size = si->green_size =
+                       si->blue_size = si->rsvd_size = 8;
+
+               si->lfb_depth = 32;
+               si->lfb_linelength = pixels_per_scan_line * 4;
        }
 }
 
-static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
-                             unsigned long size, void **handles)
+static efi_graphics_output_protocol_t *
+find_gop(efi_guid_t *proto, unsigned long size, void **handles)
 {
-       efi_graphics_output_protocol_t *gop, *first_gop;
-       u16 width, height;
-       u32 pixels_per_scan_line;
-       u32 ext_lfb_base;
-       efi_physical_addr_t fb_base;
-       efi_pixel_bitmask_t pixel_info;
-       int pixel_format;
-       efi_status_t status;
+       efi_graphics_output_protocol_t *first_gop;
        efi_handle_t h;
        int i;
 
        first_gop = NULL;
-       gop = NULL;
 
        for_each_efi_handle(h, handles, size, i) {
+               efi_status_t status;
+
+               efi_graphics_output_protocol_t *gop;
                efi_graphics_output_protocol_mode_t *mode;
-               efi_graphics_output_mode_info_t *info = NULL;
+               efi_graphics_output_mode_info_t *info;
+
                efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
-               bool conout_found = false;
                void *dummy = NULL;
-               efi_physical_addr_t current_fb_base;
 
                status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
                if (status != EFI_SUCCESS)
                        continue;
 
+               mode = efi_table_attr(gop, mode);
+               info = efi_table_attr(mode, info);
+               if (info->pixel_format == PIXEL_BLT_ONLY ||
+                   info->pixel_format >= PIXEL_FORMAT_MAX)
+                       continue;
+
+               /*
+                * Systems that use the UEFI Console Splitter may
+                * provide multiple GOP devices, not all of which are
+                * backed by real hardware. The workaround is to search
+                * for a GOP implementing the ConOut protocol, and if
+                * one isn't found, to just fall back to the first GOP.
+                *
+                * Once we've found a GOP supporting ConOut,
+                * don't bother looking any further.
+                */
                status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
                if (status == EFI_SUCCESS)
-                       conout_found = true;
+                       return gop;
 
-               mode = efi_table_attr(gop, mode);
-               info = efi_table_attr(mode, info);
-               current_fb_base = efi_table_attr(mode, frame_buffer_base);
-
-               if ((!first_gop || conout_found) &&
-                   info->pixel_format != PIXEL_BLT_ONLY) {
-                       /*
-                        * Systems that use the UEFI Console Splitter may
-                        * provide multiple GOP devices, not all of which are
-                        * backed by real hardware. The workaround is to search
-                        * for a GOP implementing the ConOut protocol, and if
-                        * one isn't found, to just fall back to the first GOP.
-                        */
-                       width = info->horizontal_resolution;
-                       height = info->vertical_resolution;
-                       pixel_format = info->pixel_format;
-                       pixel_info = info->pixel_information;
-                       pixels_per_scan_line = info->pixels_per_scan_line;
-                       fb_base = current_fb_base;
-
-                       /*
-                        * Once we've found a GOP supporting ConOut,
-                        * don't bother looking any further.
-                        */
+               if (!first_gop)
                        first_gop = gop;
-                       if (conout_found)
-                               break;
-               }
        }
 
+       return first_gop;
+}
+
+static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
+                             unsigned long size, void **handles)
+{
+       efi_graphics_output_protocol_t *gop;
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+
+       gop = find_gop(proto, size, handles);
+
        /* Did we find any GOPs? */
-       if (!first_gop)
+       if (!gop)
                return EFI_NOT_FOUND;
 
+       /* Change mode if requested */
+       set_mode(gop);
+
        /* EFI framebuffer */
+       mode = efi_table_attr(gop, mode);
+       info = efi_table_attr(mode, info);
+
        si->orig_video_isVGA = VIDEO_TYPE_EFI;
 
-       si->lfb_width = width;
-       si->lfb_height = height;
-       si->lfb_base = fb_base;
+       si->lfb_width  = info->horizontal_resolution;
+       si->lfb_height = info->vertical_resolution;
 
-       ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
-       if (ext_lfb_base) {
+       efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
+                         &si->lfb_base, &si->ext_lfb_base);
+       if (si->ext_lfb_base)
                si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
-               si->ext_lfb_base = ext_lfb_base;
-       }
 
        si->pages = 1;
 
-       setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
+       setup_pixel_info(si, info->pixels_per_scan_line,
+                            info->pixel_information, info->pixel_format);
 
        si->lfb_size = si->lfb_linelength * si->lfb_height;
 
index 09f4fa01914eea7fd76bb4bcc73ea95fe1e22c5b..feef8d4be113b653d8216347ed81e7beaf55396e 100644 (file)
@@ -91,120 +91,23 @@ fail:
 efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr,
                                unsigned long max)
 {
-       efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
-       int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1;
+       efi_physical_addr_t alloc_addr;
        efi_status_t status;
 
-       size = round_up(size, EFI_ALLOC_ALIGN);
+       if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE)
+               return efi_allocate_pages_aligned(size, addr, max,
+                                                 EFI_ALLOC_ALIGN);
+
+       alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1;
        status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS,
-                            EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack,
+                            EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE),
                             &alloc_addr);
        if (status != EFI_SUCCESS)
                return status;
 
-       *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN);
-
-       if (slack > 0) {
-               int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
-
-               if (l) {
-                       efi_bs_call(free_pages, alloc_addr, slack - l + 1);
-                       slack = l - 1;
-               }
-               if (slack)
-                       efi_bs_call(free_pages, *addr + size, slack);
-       }
+       *addr = alloc_addr;
        return EFI_SUCCESS;
 }
-/**
- * efi_low_alloc_above() - allocate pages at or above given address
- * @size:      size of the memory area to allocate
- * @align:     minimum alignment of the allocated memory area. It should
- *             a power of two.
- * @addr:      on exit the address of the allocated memory
- * @min:       minimum address to used for the memory allocation
- *
- * Allocate at the lowest possible address that is not below @min as
- * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
- * least EFI_ALLOC_ALIGN. The first allocated page will not below the address
- * given by @min.
- *
- * Return:     status code
- */
-efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
-                                unsigned long *addr, unsigned long min)
-{
-       unsigned long map_size, desc_size, buff_size;
-       efi_memory_desc_t *map;
-       efi_status_t status;
-       unsigned long nr_pages;
-       int i;
-       struct efi_boot_memmap boot_map;
-
-       boot_map.map            = &map;
-       boot_map.map_size       = &map_size;
-       boot_map.desc_size      = &desc_size;
-       boot_map.desc_ver       = NULL;
-       boot_map.key_ptr        = NULL;
-       boot_map.buff_size      = &buff_size;
-
-       status = efi_get_memory_map(&boot_map);
-       if (status != EFI_SUCCESS)
-               goto fail;
-
-       /*
-        * Enforce minimum alignment that EFI or Linux requires when
-        * requesting a specific address.  We are doing page-based (or
-        * larger) allocations, and both the address and size must meet
-        * alignment constraints.
-        */
-       if (align < EFI_ALLOC_ALIGN)
-               align = EFI_ALLOC_ALIGN;
-
-       size = round_up(size, EFI_ALLOC_ALIGN);
-       nr_pages = size / EFI_PAGE_SIZE;
-       for (i = 0; i < map_size / desc_size; i++) {
-               efi_memory_desc_t *desc;
-               unsigned long m = (unsigned long)map;
-               u64 start, end;
-
-               desc = efi_early_memdesc_ptr(m, desc_size, i);
-
-               if (desc->type != EFI_CONVENTIONAL_MEMORY)
-                       continue;
-
-               if (efi_soft_reserve_enabled() &&
-                   (desc->attribute & EFI_MEMORY_SP))
-                       continue;
-
-               if (desc->num_pages < nr_pages)
-                       continue;
-
-               start = desc->phys_addr;
-               end = start + desc->num_pages * EFI_PAGE_SIZE;
-
-               if (start < min)
-                       start = min;
-
-               start = round_up(start, align);
-               if ((start + size) > end)
-                       continue;
-
-               status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
-                                    EFI_LOADER_DATA, nr_pages, &start);
-               if (status == EFI_SUCCESS) {
-                       *addr = start;
-                       break;
-               }
-       }
-
-       if (i == map_size / desc_size)
-               status = EFI_NOT_FOUND;
-
-       efi_bs_call(free_pool, map);
-fail:
-       return status;
-}
 
 /**
  * efi_free() - free memory pages
@@ -227,81 +130,3 @@ void efi_free(unsigned long size, unsigned long addr)
        nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
        efi_bs_call(free_pages, addr, nr_pages);
 }
-
-/**
- * efi_relocate_kernel() - copy memory area
- * @image_addr:                pointer to address of memory area to copy
- * @image_size:                size of memory area to copy
- * @alloc_size:                minimum size of memory to allocate, must be greater or
- *                     equal to image_size
- * @preferred_addr:    preferred target address
- * @alignment:         minimum alignment of the allocated memory area. It
- *                     should be a power of two.
- * @min_addr:          minimum target address
- *
- * Copy a memory area to a newly allocated memory area aligned according
- * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
- * is not available, the allocated address will not be below @min_addr.
- * On exit, @image_addr is updated to the target copy address that was used.
- *
- * This function is used to copy the Linux kernel verbatim. It does not apply
- * any relocation changes.
- *
- * Return:             status code
- */
-efi_status_t efi_relocate_kernel(unsigned long *image_addr,
-                                unsigned long image_size,
-                                unsigned long alloc_size,
-                                unsigned long preferred_addr,
-                                unsigned long alignment,
-                                unsigned long min_addr)
-{
-       unsigned long cur_image_addr;
-       unsigned long new_addr = 0;
-       efi_status_t status;
-       unsigned long nr_pages;
-       efi_physical_addr_t efi_addr = preferred_addr;
-
-       if (!image_addr || !image_size || !alloc_size)
-               return EFI_INVALID_PARAMETER;
-       if (alloc_size < image_size)
-               return EFI_INVALID_PARAMETER;
-
-       cur_image_addr = *image_addr;
-
-       /*
-        * The EFI firmware loader could have placed the kernel image
-        * anywhere in memory, but the kernel has restrictions on the
-        * max physical address it can run at.  Some architectures
-        * also have a prefered address, so first try to relocate
-        * to the preferred address.  If that fails, allocate as low
-        * as possible while respecting the required alignment.
-        */
-       nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
-       status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
-                            EFI_LOADER_DATA, nr_pages, &efi_addr);
-       new_addr = efi_addr;
-       /*
-        * If preferred address allocation failed allocate as low as
-        * possible.
-        */
-       if (status != EFI_SUCCESS) {
-               status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
-                                            min_addr);
-       }
-       if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to allocate usable memory for kernel.\n");
-               return status;
-       }
-
-       /*
-        * We know source/dest won't overlap since both memory ranges
-        * have been allocated by UEFI, so we can safely use memcpy.
-        */
-       memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
-
-       /* Return the new address of the relocated image. */
-       *image_addr = new_addr;
-
-       return status;
-}
index b025e59b94dfa7a407fd8b1b83646bf7c9e72862..99fb25d2bcf54ce8af04420d02486846b7566a1c 100644 (file)
@@ -28,21 +28,21 @@ void efi_pci_disable_bridge_busmaster(void)
 
        if (status != EFI_BUFFER_TOO_SMALL) {
                if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
-                       pr_efi_err("Failed to locate PCI I/O handles'\n");
+                       efi_err("Failed to locate PCI I/O handles'\n");
                return;
        }
 
        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size,
                             (void **)&pci_handle);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to allocate memory for 'pci_handle'\n");
+               efi_err("Failed to allocate memory for 'pci_handle'\n");
                return;
        }
 
        status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto,
                             NULL, &pci_handle_size, pci_handle);
        if (status != EFI_SUCCESS) {
-               pr_efi_err("Failed to locate PCI I/O handles'\n");
+               efi_err("Failed to locate PCI I/O handles'\n");
                goto free_handle;
        }
 
@@ -69,7 +69,7 @@ void efi_pci_disable_bridge_busmaster(void)
                 * access to the framebuffer. Drivers for true PCIe graphics
                 * controllers that are behind a PCIe root port do not use
                 * DMA to implement the GOP framebuffer anyway [although they
-                * may use it in their implentation of Gop->Blt()], and so
+                * may use it in their implementation of Gop->Blt()], and so
                 * disabling DMA in the PCI bridge should not interfere with
                 * normal operation of the device.
                 */
@@ -106,7 +106,7 @@ void efi_pci_disable_bridge_busmaster(void)
                status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
                                        PCI_COMMAND, 1, &command);
                if (status != EFI_SUCCESS)
-                       pr_efi_err("Failed to disable PCI busmastering\n");
+                       efi_err("Failed to disable PCI busmastering\n");
        }
 
 free_handle:
index 4578f59e160c77b888a785913b069fe2c8d820c3..a408df474d83763526168234a9ca1577b287d1c6 100644 (file)
@@ -74,6 +74,8 @@ efi_status_t efi_random_alloc(unsigned long size,
        if (align < EFI_ALLOC_ALIGN)
                align = EFI_ALLOC_ALIGN;
 
+       size = round_up(size, EFI_ALLOC_ALIGN);
+
        /* count the suitable slots in each memory map entry */
        for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
                efi_memory_desc_t *md = (void *)memory_map + map_offset;
@@ -85,7 +87,7 @@ efi_status_t efi_random_alloc(unsigned long size,
        }
 
        /* find a random number between 0 and total_slots */
-       target_slot = (total_slots * (u16)random_seed) >> 16;
+       target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
 
        /*
         * target_slot is now a value in the range [0, total_slots), and so
@@ -109,7 +111,7 @@ efi_status_t efi_random_alloc(unsigned long size,
                }
 
                target = round_up(md->phys_addr, align) + target_slot * align;
-               pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+               pages = size / EFI_PAGE_SIZE;
 
                status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
                                     EFI_LOADER_DATA, pages, &target);
diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c
new file mode 100644 (file)
index 0000000..9b1aaf8
--- /dev/null
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/efi.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+/**
+ * efi_low_alloc_above() - allocate pages at or above given address
+ * @size:      size of the memory area to allocate
+ * @align:     minimum alignment of the allocated memory area. It should
+ *             a power of two.
+ * @addr:      on exit the address of the allocated memory
+ * @min:       minimum address to used for the memory allocation
+ *
+ * Allocate at the lowest possible address that is not below @min as
+ * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at
+ * least EFI_ALLOC_ALIGN. The first allocated page will not below the address
+ * given by @min.
+ *
+ * Return:     status code
+ */
+static efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align,
+                                       unsigned long *addr, unsigned long min)
+{
+       unsigned long map_size, desc_size, buff_size;
+       efi_memory_desc_t *map;
+       efi_status_t status;
+       unsigned long nr_pages;
+       int i;
+       struct efi_boot_memmap boot_map;
+
+       boot_map.map            = &map;
+       boot_map.map_size       = &map_size;
+       boot_map.desc_size      = &desc_size;
+       boot_map.desc_ver       = NULL;
+       boot_map.key_ptr        = NULL;
+       boot_map.buff_size      = &buff_size;
+
+       status = efi_get_memory_map(&boot_map);
+       if (status != EFI_SUCCESS)
+               goto fail;
+
+       /*
+        * Enforce minimum alignment that EFI or Linux requires when
+        * requesting a specific address.  We are doing page-based (or
+        * larger) allocations, and both the address and size must meet
+        * alignment constraints.
+        */
+       if (align < EFI_ALLOC_ALIGN)
+               align = EFI_ALLOC_ALIGN;
+
+       size = round_up(size, EFI_ALLOC_ALIGN);
+       nr_pages = size / EFI_PAGE_SIZE;
+       for (i = 0; i < map_size / desc_size; i++) {
+               efi_memory_desc_t *desc;
+               unsigned long m = (unsigned long)map;
+               u64 start, end;
+
+               desc = efi_early_memdesc_ptr(m, desc_size, i);
+
+               if (desc->type != EFI_CONVENTIONAL_MEMORY)
+                       continue;
+
+               if (efi_soft_reserve_enabled() &&
+                   (desc->attribute & EFI_MEMORY_SP))
+                       continue;
+
+               if (desc->num_pages < nr_pages)
+                       continue;
+
+               start = desc->phys_addr;
+               end = start + desc->num_pages * EFI_PAGE_SIZE;
+
+               if (start < min)
+                       start = min;
+
+               start = round_up(start, align);
+               if ((start + size) > end)
+                       continue;
+
+               status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
+                                    EFI_LOADER_DATA, nr_pages, &start);
+               if (status == EFI_SUCCESS) {
+                       *addr = start;
+                       break;
+               }
+       }
+
+       if (i == map_size / desc_size)
+               status = EFI_NOT_FOUND;
+
+       efi_bs_call(free_pool, map);
+fail:
+       return status;
+}
+
+/**
+ * efi_relocate_kernel() - copy memory area
+ * @image_addr:                pointer to address of memory area to copy
+ * @image_size:                size of memory area to copy
+ * @alloc_size:                minimum size of memory to allocate, must be greater or
+ *                     equal to image_size
+ * @preferred_addr:    preferred target address
+ * @alignment:         minimum alignment of the allocated memory area. It
+ *                     should be a power of two.
+ * @min_addr:          minimum target address
+ *
+ * Copy a memory area to a newly allocated memory area aligned according
+ * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address
+ * is not available, the allocated address will not be below @min_addr.
+ * On exit, @image_addr is updated to the target copy address that was used.
+ *
+ * This function is used to copy the Linux kernel verbatim. It does not apply
+ * any relocation changes.
+ *
+ * Return:             status code
+ */
+efi_status_t efi_relocate_kernel(unsigned long *image_addr,
+                                unsigned long image_size,
+                                unsigned long alloc_size,
+                                unsigned long preferred_addr,
+                                unsigned long alignment,
+                                unsigned long min_addr)
+{
+       unsigned long cur_image_addr;
+       unsigned long new_addr = 0;
+       efi_status_t status;
+       unsigned long nr_pages;
+       efi_physical_addr_t efi_addr = preferred_addr;
+
+       if (!image_addr || !image_size || !alloc_size)
+               return EFI_INVALID_PARAMETER;
+       if (alloc_size < image_size)
+               return EFI_INVALID_PARAMETER;
+
+       cur_image_addr = *image_addr;
+
+       /*
+        * The EFI firmware loader could have placed the kernel image
+        * anywhere in memory, but the kernel has restrictions on the
+        * max physical address it can run at.  Some architectures
+        * also have a preferred address, so first try to relocate
+        * to the preferred address.  If that fails, allocate as low
+        * as possible while respecting the required alignment.
+        */
+       nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
+       status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
+                            EFI_LOADER_DATA, nr_pages, &efi_addr);
+       new_addr = efi_addr;
+       /*
+        * If preferred address allocation failed allocate as low as
+        * possible.
+        */
+       if (status != EFI_SUCCESS) {
+               status = efi_low_alloc_above(alloc_size, alignment, &new_addr,
+                                            min_addr);
+       }
+       if (status != EFI_SUCCESS) {
+               efi_err("Failed to allocate usable memory for kernel.\n");
+               return status;
+       }
+
+       /*
+        * We know source/dest won't overlap since both memory ranges
+        * have been allocated by UEFI, so we can safely use memcpy.
+        */
+       memcpy((void *)new_addr, (void *)cur_image_addr, image_size);
+
+       /* Return the new address of the relocated image. */
+       *image_addr = new_addr;
+
+       return status;
+}
index a765378ad18c726ee18ae3fc3cf07e58412f4c60..5efc524b14bef535e274f5cf93bde867e3f1036c 100644 (file)
@@ -67,10 +67,10 @@ enum efi_secureboot_mode efi_get_secureboot(void)
                return efi_secureboot_mode_disabled;
 
 secure_boot_enabled:
-       pr_efi("UEFI Secure Boot is enabled.\n");
+       efi_info("UEFI Secure Boot is enabled.\n");
        return efi_secureboot_mode_enabled;
 
 out_efi_err:
-       pr_efi_err("Could not determine UEFI Secure Boot status.\n");
+       efi_err("Could not determine UEFI Secure Boot status.\n");
        return efi_secureboot_mode_unknown;
 }
index e9a684637b703d09f591641bf3c056e0735abb24..7acbac16eae0b25c25e6a6022546cd99fe2f632e 100644 (file)
@@ -119,7 +119,7 @@ void efi_retrieve_tpm2_eventlog(void)
                             sizeof(*log_tbl) + log_size, (void **)&log_tbl);
 
        if (status != EFI_SUCCESS) {
-               efi_printk("Unable to allocate memory for event log\n");
+               efi_err("Unable to allocate memory for event log\n");
                return;
        }
 
diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c
new file mode 100644 (file)
index 0000000..e65ef49
--- /dev/null
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ *   Copyright (C) 1991, 1992 Linus Torvalds
+ *   Copyright 2007 rPath, Inc. - All Rights Reserved
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Oh, it's a waste of space, but oh-so-yummy for debugging.
+ */
+
+#include <stdarg.h>
+
+#include <linux/compiler.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+static
+int skip_atoi(const char **s)
+{
+       int i = 0;
+
+       while (isdigit(**s))
+               i = i * 10 + *((*s)++) - '0';
+       return i;
+}
+
+/*
+ * put_dec_full4 handles numbers in the range 0 <= r < 10000.
+ * The multiplier 0xccd is round(2^15/10), and the approximation
+ * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.
+ */
+static
+void put_dec_full4(char *end, unsigned int r)
+{
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               unsigned int q = (r * 0xccd) >> 15;
+               *--end = '0' + (r - q * 10);
+               r = q;
+       }
+       *--end = '0' + r;
+}
+
+/* put_dec is copied from lib/vsprintf.c with small modifications */
+
+/*
+ * Call put_dec_full4 on x % 10000, return x / 10000.
+ * The approximation x/10000 == (x * 0x346DC5D7) >> 43
+ * holds for all x < 1,128,869,999.  The largest value this
+ * helper will ever be asked to convert is 1,125,520,955.
+ * (second call in the put_dec code, assuming n is all-ones).
+ */
+static
+unsigned int put_dec_helper4(char *end, unsigned int x)
+{
+       unsigned int q = (x * 0x346DC5D7ULL) >> 43;
+
+       put_dec_full4(end, x - q * 10000);
+       return q;
+}
+
+/* Based on code by Douglas W. Jones found at
+ * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
+ * (with permission from the author).
+ * Performs no 64-bit division and hence should be fast on 32-bit machines.
+ */
+static
+char *put_dec(char *end, unsigned long long n)
+{
+       unsigned int d3, d2, d1, q, h;
+       char *p = end;
+
+       d1  = ((unsigned int)n >> 16); /* implicit "& 0xffff" */
+       h   = (n >> 32);
+       d2  = (h      ) & 0xffff;
+       d3  = (h >> 16); /* implicit "& 0xffff" */
+
+       /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
+            = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
+       q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);
+       q = put_dec_helper4(p, q);
+       p -= 4;
+
+       q += 7671 * d3 + 9496 * d2 + 6 * d1;
+       q = put_dec_helper4(p, q);
+       p -= 4;
+
+       q += 4749 * d3 + 42 * d2;
+       q = put_dec_helper4(p, q);
+       p -= 4;
+
+       q += 281 * d3;
+       q = put_dec_helper4(p, q);
+       p -= 4;
+
+       put_dec_full4(p, q);
+       p -= 4;
+
+       /* strip off the extra 0's we printed */
+       while (p < end && *p == '0')
+               ++p;
+
+       return p;
+}
+
+static
+char *number(char *end, unsigned long long num, int base, char locase)
+{
+       /*
+        * locase = 0 or 0x20. ORing digits or letters with 'locase'
+        * produces same digits or (maybe lowercased) letters
+        */
+
+       /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
+       static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
+
+       switch (base) {
+       case 10:
+               if (num != 0)
+                       end = put_dec(end, num);
+               break;
+       case 8:
+               for (; num != 0; num >>= 3)
+                       *--end = '0' + (num & 07);
+               break;
+       case 16:
+               for (; num != 0; num >>= 4)
+                       *--end = digits[num & 0xf] | locase;
+               break;
+       default:
+               unreachable();
+       };
+
+       return end;
+}
+
+#define ZEROPAD        1               /* pad with zero */
+#define SIGN   2               /* unsigned/signed long */
+#define PLUS   4               /* show plus */
+#define SPACE  8               /* space if plus */
+#define LEFT   16              /* left justified */
+#define SMALL  32              /* Must be 32 == 0x20 */
+#define SPECIAL        64              /* 0x */
+#define WIDE   128             /* UTF-16 string */
+
+static
+int get_flags(const char **fmt)
+{
+       int flags = 0;
+
+       do {
+               switch (**fmt) {
+               case '-':
+                       flags |= LEFT;
+                       break;
+               case '+':
+                       flags |= PLUS;
+                       break;
+               case ' ':
+                       flags |= SPACE;
+                       break;
+               case '#':
+                       flags |= SPECIAL;
+                       break;
+               case '0':
+                       flags |= ZEROPAD;
+                       break;
+               default:
+                       return flags;
+               }
+               ++(*fmt);
+       } while (1);
+}
+
+static
+int get_int(const char **fmt, va_list *ap)
+{
+       if (isdigit(**fmt))
+               return skip_atoi(fmt);
+       if (**fmt == '*') {
+               ++(*fmt);
+               /* it's the next argument */
+               return va_arg(*ap, int);
+       }
+       return 0;
+}
+
+static
+unsigned long long get_number(int sign, int qualifier, va_list *ap)
+{
+       if (sign) {
+               switch (qualifier) {
+               case 'L':
+                       return va_arg(*ap, long long);
+               case 'l':
+                       return va_arg(*ap, long);
+               case 'h':
+                       return (short)va_arg(*ap, int);
+               case 'H':
+                       return (signed char)va_arg(*ap, int);
+               default:
+                       return va_arg(*ap, int);
+               };
+       } else {
+               switch (qualifier) {
+               case 'L':
+                       return va_arg(*ap, unsigned long long);
+               case 'l':
+                       return va_arg(*ap, unsigned long);
+               case 'h':
+                       return (unsigned short)va_arg(*ap, int);
+               case 'H':
+                       return (unsigned char)va_arg(*ap, int);
+               default:
+                       return va_arg(*ap, unsigned int);
+               }
+       }
+}
+
+static
+char get_sign(long long *num, int flags)
+{
+       if (!(flags & SIGN))
+               return 0;
+       if (*num < 0) {
+               *num = -(*num);
+               return '-';
+       }
+       if (flags & PLUS)
+               return '+';
+       if (flags & SPACE)
+               return ' ';
+       return 0;
+}
+
+static
+size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)
+{
+       size_t len, clen;
+
+       for (len = 0; len < maxlen && *s16; len += clen) {
+               u16 c0 = *s16++;
+
+               /* First, get the length for a BMP character */
+               clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);
+               if (len + clen > maxlen)
+                       break;
+               /*
+                * If this is a high surrogate, and we're already at maxlen, we
+                * can't include the character if it's a valid surrogate pair.
+                * Avoid accessing one extra word just to check if it's valid
+                * or not.
+                */
+               if ((c0 & 0xfc00) == 0xd800) {
+                       if (len + clen == maxlen)
+                               break;
+                       if ((*s16 & 0xfc00) == 0xdc00) {
+                               ++s16;
+                               ++clen;
+                       }
+               }
+       }
+
+       return len;
+}
+
+static
+u32 utf16_to_utf32(const u16 **s16)
+{
+       u16 c0, c1;
+
+       c0 = *(*s16)++;
+       /* not a surrogate */
+       if ((c0 & 0xf800) != 0xd800)
+               return c0;
+       /* invalid: low surrogate instead of high */
+       if (c0 & 0x0400)
+               return 0xfffd;
+       c1 = **s16;
+       /* invalid: missing low surrogate */
+       if ((c1 & 0xfc00) != 0xdc00)
+               return 0xfffd;
+       /* valid surrogate pair */
+       ++(*s16);
+       return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
+}
+
+#define PUTC(c) \
+do {                           \
+       if (pos < size)         \
+               buf[pos] = (c); \
+       ++pos;                  \
+} while (0);
+
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
+{
+       /* The maximum space required is to print a 64-bit number in octal */
+       char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];
+       char *tmp_end = &tmp[ARRAY_SIZE(tmp)];
+       long long num;
+       int base;
+       const char *s;
+       size_t len, pos;
+       char sign;
+
+       int flags;              /* flags to number() */
+
+       int field_width;        /* width of output field */
+       int precision;          /* min. # of digits for integers; max
+                                  number of chars for from string */
+       int qualifier;          /* 'h', 'hh', 'l' or 'll' for integer fields */
+
+       va_list args;
+
+       /*
+        * We want to pass our input va_list to helper functions by reference,
+        * but there's an annoying edge case. If va_list was originally passed
+        * to us by value, we could just pass &ap down to the helpers. This is
+        * the case on, for example, X86_32.
+        * However, on X86_64 (and possibly others), va_list is actually a
+        * size-1 array containing a structure. Our function parameter ap has
+        * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
+        * which is what will be expected by a function taking a va_list *
+        * parameter.
+        * One standard way to solve this mess is by creating a copy in a local
+        * variable of type va_list and then passing a pointer to that local
+        * copy instead, which is what we do here.
+        */
+       va_copy(args, ap);
+
+       for (pos = 0; *fmt; ++fmt) {
+               if (*fmt != '%' || *++fmt == '%') {
+                       PUTC(*fmt);
+                       continue;
+               }
+
+               /* process flags */
+               flags = get_flags(&fmt);
+
+               /* get field width */
+               field_width = get_int(&fmt, &args);
+               if (field_width < 0) {
+                       field_width = -field_width;
+                       flags |= LEFT;
+               }
+
+               if (flags & LEFT)
+                       flags &= ~ZEROPAD;
+
+               /* get the precision */
+               precision = -1;
+               if (*fmt == '.') {
+                       ++fmt;
+                       precision = get_int(&fmt, &args);
+                       if (precision >= 0)
+                               flags &= ~ZEROPAD;
+               }
+
+               /* get the conversion qualifier */
+               qualifier = -1;
+               if (*fmt == 'h' || *fmt == 'l') {
+                       qualifier = *fmt;
+                       ++fmt;
+                       if (qualifier == *fmt) {
+                               qualifier -= 'a'-'A';
+                               ++fmt;
+                       }
+               }
+
+               sign = 0;
+
+               switch (*fmt) {
+               case 'c':
+                       flags &= LEFT;
+                       s = tmp;
+                       if (qualifier == 'l') {
+                               ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);
+                               ((u16 *)tmp)[1] = L'\0';
+                               precision = INT_MAX;
+                               goto wstring;
+                       } else {
+                               tmp[0] = (unsigned char)va_arg(args, int);
+                               precision = len = 1;
+                       }
+                       goto output;
+
+               case 's':
+                       flags &= LEFT;
+                       if (precision < 0)
+                               precision = INT_MAX;
+                       s = va_arg(args, void *);
+                       if (!s)
+                               s = precision < 6 ? "" : "(null)";
+                       else if (qualifier == 'l') {
+               wstring:
+                               flags |= WIDE;
+                               precision = len = utf16s_utf8nlen((const u16 *)s, precision);
+                               goto output;
+                       }
+                       precision = len = strnlen(s, precision);
+                       goto output;
+
+                       /* integer number formats - set up the flags and "break" */
+               case 'o':
+                       base = 8;
+                       break;
+
+               case 'p':
+                       if (precision < 0)
+                               precision = 2 * sizeof(void *);
+                       fallthrough;
+               case 'x':
+                       flags |= SMALL;
+                       fallthrough;
+               case 'X':
+                       base = 16;
+                       break;
+
+               case 'd':
+               case 'i':
+                       flags |= SIGN;
+                       fallthrough;
+               case 'u':
+                       flags &= ~SPECIAL;
+                       base = 10;
+                       break;
+
+               default:
+                       /*
+                        * Bail out if the conversion specifier is invalid.
+                        * There's probably a typo in the format string and the
+                        * remaining specifiers are unlikely to match up with
+                        * the arguments.
+                        */
+                       goto fail;
+               }
+               if (*fmt == 'p') {
+                       num = (unsigned long)va_arg(args, void *);
+               } else {
+                       num = get_number(flags & SIGN, qualifier, &args);
+               }
+
+               sign = get_sign(&num, flags);
+               if (sign)
+                       --field_width;
+
+               s = number(tmp_end, num, base, flags & SMALL);
+               len = tmp_end - s;
+               /* default precision is 1 */
+               if (precision < 0)
+                       precision = 1;
+               /* precision is minimum number of digits to print */
+               if (precision < len)
+                       precision = len;
+               if (flags & SPECIAL) {
+                       /*
+                        * For octal, a leading 0 is printed only if necessary,
+                        * i.e. if it's not already there because of the
+                        * precision.
+                        */
+                       if (base == 8 && precision == len)
+                               ++precision;
+                       /*
+                        * For hexadecimal, the leading 0x is skipped if the
+                        * output is empty, i.e. both the number and the
+                        * precision are 0.
+                        */
+                       if (base == 16 && precision > 0)
+                               field_width -= 2;
+                       else
+                               flags &= ~SPECIAL;
+               }
+               /*
+                * For zero padding, increase the precision to fill the field
+                * width.
+                */
+               if ((flags & ZEROPAD) && field_width > precision)
+                       precision = field_width;
+
+output:
+               /* Calculate the padding necessary */
+               field_width -= precision;
+               /* Leading padding with ' ' */
+               if (!(flags & LEFT))
+                       while (field_width-- > 0)
+                               PUTC(' ');
+               /* sign */
+               if (sign)
+                       PUTC(sign);
+               /* 0x/0X for hexadecimal */
+               if (flags & SPECIAL) {
+                       PUTC('0');
+                       PUTC( 'X' | (flags & SMALL));
+               }
+               /* Zero padding and excess precision */
+               while (precision-- > len)
+                       PUTC('0');
+               /* Actual output */
+               if (flags & WIDE) {
+                       const u16 *ws = (const u16 *)s;
+
+                       while (len-- > 0) {
+                               u32 c32 = utf16_to_utf32(&ws);
+                               u8 *s8;
+                               size_t clen;
+
+                               if (c32 < 0x80) {
+                                       PUTC(c32);
+                                       continue;
+                               }
+
+                               /* Number of trailing octets */
+                               clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);
+
+                               len -= clen;
+                               s8 = (u8 *)&buf[pos];
+
+                               /* Avoid writing partial character */
+                               PUTC('\0');
+                               pos += clen;
+                               if (pos >= size)
+                                       continue;
+
+                               /* Set high bits of leading octet */
+                               *s8 = (0xf00 >> 1) >> clen;
+                               /* Write trailing octets in reverse order */
+                               for (s8 += clen; clen; --clen, c32 >>= 6)
+                                       *s8-- = 0x80 | (c32 & 0x3f);
+                               /* Set low bits of leading octet */
+                               *s8 |= c32;
+                       }
+               } else {
+                       while (len-- > 0)
+                               PUTC(*s++);
+               }
+               /* Trailing padding with ' ' */
+               while (field_width-- > 0)
+                       PUTC(' ');
+       }
+fail:
+       va_end(args);
+
+       if (size)
+               buf[min(pos, size-1)] = '\0';
+
+       return pos;
+}
+
+int snprintf(char *buf, size_t size, const char *fmt, ...)
+{
+       va_list args;
+       int i;
+
+       va_start(args, fmt);
+       i = vsnprintf(buf, size, fmt, args);
+       va_end(args);
+       return i;
+}
index f0339b5d3658dc8d282484a93e825e8ac962c896..5a48d996ed710471a1c75531472f4806c59b3d9a 100644 (file)
 /* Maximum physical address for 64-bit kernel with 4-level paging */
 #define MAXMEM_X86_64_4LEVEL (1ull << 46)
 
-static efi_system_table_t *sys_table __efistub_global;
-extern const bool efi_is64;
+const efi_system_table_t *efi_system_table;
 extern u32 image_offset;
-
-__pure efi_system_table_t *efi_system_table(void)
-{
-       return sys_table;
-}
-
-__attribute_const__ bool efi_is_64bit(void)
-{
-       if (IS_ENABLED(CONFIG_EFI_MIXED))
-               return efi_is64;
-       return IS_ENABLED(CONFIG_X86_64);
-}
+static efi_loaded_image_t *image = NULL;
 
 static efi_status_t
 preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
@@ -62,7 +50,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
        status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
                             (void **)&rom);
        if (status != EFI_SUCCESS) {
-               efi_printk("Failed to allocate memory for 'rom'\n");
+               efi_err("Failed to allocate memory for 'rom'\n");
                return status;
        }
 
@@ -78,7 +66,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
                                PCI_VENDOR_ID, 1, &rom->vendor);
 
        if (status != EFI_SUCCESS) {
-               efi_printk("Failed to read rom->vendor\n");
+               efi_err("Failed to read rom->vendor\n");
                goto free_struct;
        }
 
@@ -86,7 +74,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
                                PCI_DEVICE_ID, 1, &rom->devid);
 
        if (status != EFI_SUCCESS) {
-               efi_printk("Failed to read rom->devid\n");
+               efi_err("Failed to read rom->devid\n");
                goto free_struct;
        }
 
@@ -131,7 +119,7 @@ static void setup_efi_pci(struct boot_params *params)
                                     (void **)&pci_handle);
 
                if (status != EFI_SUCCESS) {
-                       efi_printk("Failed to allocate memory for 'pci_handle'\n");
+                       efi_err("Failed to allocate memory for 'pci_handle'\n");
                        return;
                }
 
@@ -185,7 +173,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
                return;
 
        if (efi_table_attr(p, version) != 0x10000) {
-               efi_printk("Unsupported properties proto version\n");
+               efi_err("Unsupported properties proto version\n");
                return;
        }
 
@@ -198,7 +186,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
                                     size + sizeof(struct setup_data),
                                     (void **)&new);
                if (status != EFI_SUCCESS) {
-                       efi_printk("Failed to allocate memory for 'properties'\n");
+                       efi_err("Failed to allocate memory for 'properties'\n");
                        return;
                }
 
@@ -227,7 +215,7 @@ static const efi_char16_t apple[] = L"Apple";
 static void setup_quirks(struct boot_params *boot_params)
 {
        efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
-               efi_table_attr(efi_system_table(), fw_vendor);
+               efi_table_attr(efi_system_table, fw_vendor);
 
        if (!memcmp(fw_vendor, apple, sizeof(apple))) {
                if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
@@ -368,7 +356,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
 {
        struct boot_params *boot_params;
        struct setup_header *hdr;
-       efi_loaded_image_t *image;
        void *image_base;
        efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
        int options_size = 0;
@@ -377,28 +364,29 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        unsigned long ramdisk_addr;
        unsigned long ramdisk_size;
 
-       sys_table = sys_table_arg;
+       efi_system_table = sys_table_arg;
 
        /* Check if we were booted by the EFI firmware */
-       if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+       if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
                efi_exit(handle, EFI_INVALID_PARAMETER);
 
        status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image);
        if (status != EFI_SUCCESS) {
-               efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+               efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
                efi_exit(handle, status);
        }
 
        image_base = efi_table_attr(image, image_base);
        image_offset = (void *)startup_32 - image_base;
 
-       status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
+       status = efi_allocate_pages(sizeof(struct boot_params),
+                                   (unsigned long *)&boot_params, ULONG_MAX);
        if (status != EFI_SUCCESS) {
-               efi_printk("Failed to allocate lowmem for boot params\n");
+               efi_err("Failed to allocate lowmem for boot params\n");
                efi_exit(handle, status);
        }
 
-       memset(boot_params, 0x0, 0x4000);
+       memset(boot_params, 0x0, sizeof(struct boot_params));
 
        hdr = &boot_params->hdr;
 
@@ -416,43 +404,21 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        hdr->type_of_loader = 0x21;
 
        /* Convert unicode cmdline to ascii */
-       cmdline_ptr = efi_convert_cmdline(image, &options_size, ULONG_MAX);
+       cmdline_ptr = efi_convert_cmdline(image, &options_size);
        if (!cmdline_ptr)
                goto fail;
 
-       hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
-       /* Fill in upper bits of command line address, NOP on 32 bit  */
-       boot_params->ext_cmd_line_ptr = (u64)(unsigned long)cmdline_ptr >> 32;
+       efi_set_u64_split((unsigned long)cmdline_ptr,
+                         &hdr->cmd_line_ptr, &boot_params->ext_cmd_line_ptr);
 
        hdr->ramdisk_image = 0;
        hdr->ramdisk_size = 0;
 
-       if (efi_is_native()) {
-               status = efi_parse_options(cmdline_ptr);
-               if (status != EFI_SUCCESS)
-                       goto fail2;
-
-               if (!noinitrd()) {
-                       status = efi_load_initrd(image, &ramdisk_addr,
-                                                &ramdisk_size,
-                                                hdr->initrd_addr_max,
-                                                ULONG_MAX);
-                       if (status != EFI_SUCCESS)
-                               goto fail2;
-                       hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
-                       hdr->ramdisk_size  = ramdisk_size & 0xffffffff;
-                       boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
-                       boot_params->ext_ramdisk_size  = (u64)ramdisk_size >> 32;
-               }
-       }
-
-       efi_stub_entry(handle, sys_table, boot_params);
+       efi_stub_entry(handle, sys_table_arg, boot_params);
        /* not reached */
 
-fail2:
-       efi_free(options_size, (unsigned long)cmdline_ptr);
 fail:
-       efi_free(0x4000, (unsigned long)boot_params);
+       efi_free(sizeof(struct boot_params), (unsigned long)boot_params);
 
        efi_exit(handle, status);
 }
@@ -645,17 +611,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
                                   : EFI32_LOADER_SIGNATURE;
        memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32));
 
-       p->efi->efi_systab              = (unsigned long)efi_system_table();
+       efi_set_u64_split((unsigned long)efi_system_table,
+                         &p->efi->efi_systab, &p->efi->efi_systab_hi);
        p->efi->efi_memdesc_size        = *map->desc_size;
        p->efi->efi_memdesc_version     = *map->desc_ver;
-       p->efi->efi_memmap              = (unsigned long)*map->map;
+       efi_set_u64_split((unsigned long)*map->map,
+                         &p->efi->efi_memmap, &p->efi->efi_memmap_hi);
        p->efi->efi_memmap_size         = *map->map_size;
 
-#ifdef CONFIG_X86_64
-       p->efi->efi_systab_hi           = (unsigned long)efi_system_table() >> 32;
-       p->efi->efi_memmap_hi           = (unsigned long)*map->map >> 32;
-#endif
-
        return EFI_SUCCESS;
 }
 
@@ -711,12 +674,11 @@ unsigned long efi_main(efi_handle_t handle,
        unsigned long buffer_start, buffer_end;
        struct setup_header *hdr = &boot_params->hdr;
        efi_status_t status;
-       unsigned long cmdline_paddr;
 
-       sys_table = sys_table_arg;
+       efi_system_table = sys_table_arg;
 
        /* Check if we were booted by the EFI firmware */
-       if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+       if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
                efi_exit(handle, EFI_INVALID_PARAMETER);
 
        /*
@@ -759,7 +721,7 @@ unsigned long efi_main(efi_handle_t handle,
                                             hdr->kernel_alignment,
                                             LOAD_PHYSICAL_ADDR);
                if (status != EFI_SUCCESS) {
-                       efi_printk("efi_relocate_kernel() failed!\n");
+                       efi_err("efi_relocate_kernel() failed!\n");
                        goto fail;
                }
                /*
@@ -770,35 +732,48 @@ unsigned long efi_main(efi_handle_t handle,
                image_offset = 0;
        }
 
-       /*
-        * efi_pe_entry() may have been called before efi_main(), in which
-        * case this is the second time we parse the cmdline. This is ok,
-        * parsing the cmdline multiple times does not have side-effects.
-        */
-       cmdline_paddr = ((u64)hdr->cmd_line_ptr |
-                        ((u64)boot_params->ext_cmd_line_ptr << 32));
-       efi_parse_options((char *)cmdline_paddr);
+#ifdef CONFIG_CMDLINE_BOOL
+       status = efi_parse_options(CONFIG_CMDLINE);
+       if (status != EFI_SUCCESS) {
+               efi_err("Failed to parse options\n");
+               goto fail;
+       }
+#endif
+       if (!IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) {
+               unsigned long cmdline_paddr = ((u64)hdr->cmd_line_ptr |
+                                              ((u64)boot_params->ext_cmd_line_ptr << 32));
+               status = efi_parse_options((char *)cmdline_paddr);
+               if (status != EFI_SUCCESS) {
+                       efi_err("Failed to parse options\n");
+                       goto fail;
+               }
+       }
 
        /*
-        * At this point, an initrd may already have been loaded, either by
-        * the bootloader and passed via bootparams, or loaded from a initrd=
-        * command line option by efi_pe_entry() above. In either case, we
-        * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device
-        * path to supersede it.
+        * At this point, an initrd may already have been loaded by the
+        * bootloader and passed via bootparams. We permit an initrd loaded
+        * from the LINUX_EFI_INITRD_MEDIA_GUID device path to supersede it.
+        *
+        * If the device path is not present, any command-line initrd=
+        * arguments will be processed only if image is not NULL, which will be
+        * the case only if we were loaded via the PE entry point.
         */
-       if (!noinitrd()) {
+       if (!efi_noinitrd) {
                unsigned long addr, size;
 
-               status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX);
-               if (status == EFI_SUCCESS) {
-                       hdr->ramdisk_image              = (u32)addr;
-                       hdr->ramdisk_size               = (u32)size;
-                       boot_params->ext_ramdisk_image  = (u64)addr >> 32;
-                       boot_params->ext_ramdisk_size   = (u64)size >> 32;
-               } else if (status != EFI_NOT_FOUND) {
-                       efi_printk("efi_load_initrd_dev_path() failed!\n");
+               status = efi_load_initrd(image, &addr, &size,
+                                        hdr->initrd_addr_max, ULONG_MAX);
+
+               if (status != EFI_SUCCESS) {
+                       efi_err("Failed to load initrd!\n");
                        goto fail;
                }
+               if (size > 0) {
+                       efi_set_u64_split(addr, &hdr->ramdisk_image,
+                                         &boot_params->ext_ramdisk_image);
+                       efi_set_u64_split(size, &hdr->ramdisk_size,
+                                         &boot_params->ext_ramdisk_size);
+               }
        }
 
        /*
@@ -823,13 +798,13 @@ unsigned long efi_main(efi_handle_t handle,
 
        status = exit_boot(boot_params, handle);
        if (status != EFI_SUCCESS) {
-               efi_printk("exit_boot() failed!\n");
+               efi_err("exit_boot() failed!\n");
                goto fail;
        }
 
        return bzimage_addr;
 fail:
-       efi_printk("efi_main() failed!\n");
+       efi_err("efi_main() failed!\n");
 
        efi_exit(handle, status);
 }
index 7baf48c01e72f2f63e65e802609059da9cdb12ee..ddf9eae396fe1c9f3333c0c2431ec161cb8c5767 100644 (file)
@@ -70,9 +70,6 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
                return 0;
        }
 
-       if (!access_ok(src, 1))
-               return -EFAULT;
-
        buf = memdup_user(src, len);
        if (IS_ERR(buf)) {
                *dst = NULL;
@@ -91,9 +88,6 @@ copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
 static inline int
 get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
 {
-       if (!access_ok(src, 1))
-               return -EFAULT;
-
        *len = user_ucs2_strsize(src);
        if (*len == 0)
                return -EFAULT;
@@ -118,9 +112,6 @@ copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
 {
        size_t len;
 
-       if (!access_ok(src, 1))
-               return -EFAULT;
-
        len = user_ucs2_strsize(src);
        if (len == 0)
                return -EFAULT;
@@ -142,9 +133,6 @@ copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
        if (!src)
                return 0;
 
-       if (!access_ok(dst, 1))
-               return -EFAULT;
-
        return copy_to_user(dst, src, len);
 }
 
index 2937d44b5df46ef1fc437b75b134a0f11905cff1..92013ecc2d9ed71c624bf0e09a118164b48e47db 100644 (file)
  * require cooperation with a Trusted OS driver.
  */
 static int resident_cpu = -1;
+struct psci_operations psci_ops;
+static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;
 
 bool psci_tos_resident_on(int cpu)
 {
        return cpu == resident_cpu;
 }
 
-struct psci_operations psci_ops = {
-       .conduit = SMCCC_CONDUIT_NONE,
-       .smccc_version = SMCCC_VERSION_1_0,
-};
-
-enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
-{
-       if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
-               return SMCCC_CONDUIT_NONE;
-
-       return psci_ops.conduit;
-}
-
 typedef unsigned long (psci_fn)(unsigned long, unsigned long,
                                unsigned long, unsigned long);
 static psci_fn *invoke_psci_fn;
@@ -242,7 +231,7 @@ static void set_conduit(enum arm_smccc_conduit conduit)
                WARN(1, "Unexpected PSCI conduit %d\n", conduit);
        }
 
-       psci_ops.conduit = conduit;
+       psci_conduit = conduit;
 }
 
 static int get_set_conduit_method(struct device_node *np)
@@ -411,8 +400,8 @@ static void __init psci_init_smccc(void)
        if (feature != PSCI_RET_NOT_SUPPORTED) {
                u32 ret;
                ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0);
-               if (ret == ARM_SMCCC_VERSION_1_1) {
-                       psci_ops.smccc_version = SMCCC_VERSION_1_1;
+               if (ret >= ARM_SMCCC_VERSION_1_1) {
+                       arm_smccc_version_init(ret, psci_conduit);
                        ver = ret;
                }
        }
index da26a584dca0693d26e8e13d8e994bb12465794a..a3e85186f8e66075526e517581b121eb6370eb55 100644 (file)
@@ -182,16 +182,10 @@ rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
                                        RPI_FIRMWARE_GET_FIRMWARE_REVISION,
                                        &packet, sizeof(packet));
 
-       if (ret == 0) {
-               struct tm tm;
-
-               time64_to_tm(packet, 0, &tm);
+       if (ret)
+               return;
 
-               dev_info(fw->cl.dev,
-                        "Attached to firmware from %04ld-%02d-%02d %02d:%02d\n",
-                        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-                        tm.tm_hour, tm.tm_min);
-       }
+       dev_info(fw->cl.dev, "Attached to firmware from %ptT\n", &packet);
 }
 
 static void
diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig
new file mode 100644 (file)
index 0000000..27b675d
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config HAVE_ARM_SMCCC
+       bool
+       help
+         Include support for the Secure Monitor Call (SMC) and Hypervisor
+         Call (HVC) instructions on Armv7 and above architectures.
+
+config HAVE_ARM_SMCCC_DISCOVERY
+       bool
+       depends on ARM_PSCI_FW
+       default y
+       help
+        SMCCC v1.0 lacked discoverability and hence PSCI v1.0 was updated
+        to add SMCCC discovery mechanism though the PSCI firmware
+        implementation of PSCI_FEATURES(SMCCC_VERSION) which returns
+        success on firmware compliant to SMCCC v1.1 and above.
diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
new file mode 100644 (file)
index 0000000..6f369fe
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
new file mode 100644 (file)
index 0000000..4e80921
--- /dev/null
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Arm Limited
+ */
+
+#define pr_fmt(fmt) "smccc: " fmt
+
+#include <linux/init.h>
+#include <linux/arm-smccc.h>
+
+static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
+static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
+
+void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
+{
+       smccc_version = version;
+       smccc_conduit = conduit;
+}
+
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
+{
+       if (smccc_version < ARM_SMCCC_VERSION_1_1)
+               return SMCCC_CONDUIT_NONE;
+
+       return smccc_conduit;
+}
+
+u32 arm_smccc_get_version(void)
+{
+       return smccc_version;
+}
index 62f924489db593ff1aeb32dcd86ed8f18619ee71..d902acb36d1466be5d4611e852583dac80bbb229 100644 (file)
@@ -324,10 +324,6 @@ int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
        if (user_addr + length < user_addr)
                return -EINVAL;
 
-       if (!access_ok((void __user *)(unsigned long)user_addr,
-                      length))
-               return -EINVAL;
-
        region = kzalloc(sizeof(*region), GFP_KERNEL);
        if (!region)
                return -ENOMEM;
index a233a53db708175f7637470b173e4c3fe52c5b4f..1194c0e850e074b2a11b015abda75807b7090195 100644 (file)
@@ -97,10 +97,6 @@ static int fme_pr(struct platform_device *pdev, unsigned long arg)
                return -EINVAL;
        }
 
-       if (!access_ok((void __user *)(unsigned long)port_pr.buffer_address,
-                      port_pr.buffer_size))
-               return -EFAULT;
-
        /*
         * align PR buffer per PR bandwidth, as HW ignores the extra padding
         * data automatically.
index baee8c3f06ad864a9bcc6b24d36c2e414d97ff97..cf3687a7925ff0b01cf9a67190ca537f3cbf8880 100644 (file)
@@ -625,7 +625,7 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
 
        kona_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(kona_gpio->reg_base)) {
-               ret = -ENXIO;
+               ret = PTR_ERR(kona_gpio->reg_base);
                goto err_irq_domain;
        }
 
index da1ef0b1c291bd331346d58ee382feeb86861a20..b1accfba017d1571f71c55ce19ffa435c0e79e9c 100644 (file)
@@ -148,8 +148,10 @@ static int gpio_exar_probe(struct platform_device *pdev)
        mutex_init(&exar_gpio->lock);
 
        index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL);
-       if (index < 0)
-               goto err_destroy;
+       if (index < 0) {
+               ret = index;
+               goto err_mutex_destroy;
+       }
 
        sprintf(exar_gpio->name, "exar_gpio%d", index);
        exar_gpio->gpio_chip.label = exar_gpio->name;
@@ -176,6 +178,7 @@ static int gpio_exar_probe(struct platform_device *pdev)
 
 err_destroy:
        ida_simple_remove(&ida_index, index);
+err_mutex_destroy:
        mutex_destroy(&exar_gpio->lock);
        return ret;
 }
index 7b70850502192d67df4e9d6dbe5a4f7369c075cb..da570e63589d09896a6bfb9d7c24811604b0a5d8 100644 (file)
@@ -127,8 +127,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
 {
        u32 arm_gpio_lock_val;
 
-       spin_lock(&gs->gc.bgpio_lock);
        mutex_lock(yu_arm_gpio_lock_param.lock);
+       spin_lock(&gs->gc.bgpio_lock);
 
        arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io);
 
@@ -136,8 +136,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
         * When lock active bit[31] is set, ModeX is write enabled
         */
        if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) {
-               mutex_unlock(yu_arm_gpio_lock_param.lock);
                spin_unlock(&gs->gc.bgpio_lock);
+               mutex_unlock(yu_arm_gpio_lock_param.lock);
                return -EINVAL;
        }
 
@@ -152,8 +152,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
 static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs)
 {
        writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io);
-       mutex_unlock(yu_arm_gpio_lock_param.lock);
        spin_unlock(&gs->gc.bgpio_lock);
+       mutex_unlock(yu_arm_gpio_lock_param.lock);
 }
 
 /*
index 3c9f4fb3d5a28c6fccac61b805af0e319cc0737c..bd65114eb17048c40ed43cc6bbe3e6157149c2b1 100644 (file)
@@ -782,6 +782,15 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
                                     "marvell,armada-370-gpio"))
                return 0;
 
+       /*
+        * There are only two sets of PWM configuration registers for
+        * all the GPIO lines on those SoCs which this driver reserves
+        * for the first two GPIO chips. So if the resource is missing
+        * we can't treat it as an error.
+        */
+       if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"))
+               return 0;
+
        if (IS_ERR(mvchip->clk))
                return PTR_ERR(mvchip->clk);
 
@@ -804,12 +813,6 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
        mvchip->mvpwm = mvpwm;
        mvpwm->mvchip = mvchip;
 
-       /*
-        * There are only two sets of PWM configuration registers for
-        * all the GPIO lines on those SoCs which this driver reserves
-        * for the first two GPIO chips. So if the resource is missing
-        * we can't treat it as an error.
-        */
        mvpwm->membase = devm_platform_ioremap_resource_byname(pdev, "pwm");
        if (IS_ERR(mvpwm->membase))
                return PTR_ERR(mvpwm->membase);
index 1361270ecf8ce98370687a11eeec6659009ad6d5..0cb6600b8eeee88e4fc12c837d68c8c5d13a9c53 100644 (file)
@@ -660,8 +660,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
        pchip->irq1 = irq1;
 
        gpio_reg_base = devm_platform_ioremap_resource(pdev, 0);
-       if (!gpio_reg_base)
-               return -EINVAL;
+       if (IS_ERR(gpio_reg_base))
+               return PTR_ERR(gpio_reg_base);
 
        clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(clk)) {
index 182136d98b97751cb2d7d4010f524040689e3e1f..c14f0784274ae8ab7780a69791ae1682480c3f2e 100644 (file)
@@ -729,6 +729,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
                        if (ret)
                                goto out_free_descs;
                }
+
+               atomic_notifier_call_chain(&desc->gdev->notifier,
+                                          GPIOLINE_CHANGED_REQUESTED, desc);
+
                dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
                        offset);
        }
@@ -1083,6 +1087,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
        if (ret)
                goto out_free_desc;
 
+       atomic_notifier_call_chain(&desc->gdev->notifier,
+                                  GPIOLINE_CHANGED_REQUESTED, desc);
+
        le->irq = gpiod_to_irq(desc);
        if (le->irq <= 0) {
                ret = -ENODEV;
@@ -2998,8 +3005,6 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
        }
 done:
        spin_unlock_irqrestore(&gpio_lock, flags);
-       atomic_notifier_call_chain(&desc->gdev->notifier,
-                                  GPIOLINE_CHANGED_REQUESTED, desc);
        return ret;
 }
 
@@ -4215,7 +4220,9 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
                }
        }
 
-       if (test_bit(FLAG_IS_OUT, &desc->flags)) {
+       /* To be valid for IRQ the line needs to be input or open drain */
+       if (test_bit(FLAG_IS_OUT, &desc->flags) &&
+           !test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
                chip_err(gc,
                         "%s: tried to flag a GPIO set as output for IRQ\n",
                         __func__);
@@ -4278,7 +4285,12 @@ void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset)
 
        if (!IS_ERR(desc) &&
            !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) {
-               WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags));
+               /*
+                * We must not be output when using IRQ UNLESS we are
+                * open drain.
+                */
+               WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags) &&
+                       !test_bit(FLAG_OPEN_DRAIN, &desc->flags));
                set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
        }
 }
@@ -4961,6 +4973,9 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
                return ERR_PTR(ret);
        }
 
+       atomic_notifier_call_chain(&desc->gdev->notifier,
+                                  GPIOLINE_CHANGED_REQUESTED, desc);
+
        return desc;
 }
 EXPORT_SYMBOL_GPL(gpiod_get_index);
@@ -5026,6 +5041,9 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                return ERR_PTR(ret);
        }
 
+       atomic_notifier_call_chain(&desc->gdev->notifier,
+                                  GPIOLINE_CHANGED_REQUESTED, desc);
+
        return desc;
 }
 EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
index 43594978958e804807a3d681869ca4963100570c..fb92be7e8aa71b2d4f5bfa7f70fa9058a6f45240 100644 (file)
@@ -161,7 +161,7 @@ config DRM_LOAD_EDID_FIRMWARE
          monitor are unable to provide appropriate EDID data. Since this
          feature is provided as a workaround for broken hardware, the
          default case is N. Details and instructions how to build your own
-         EDID data are given in Documentation/driver-api/edid.rst.
+         EDID data are given in Documentation/admin-guide/edid.rst.
 
 config DRM_DP_CEC
        bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support"
index fd1dc3236ecae0a4c169677a39cc07fe43cb8d2a..a9086ea1ab60e9bd0047942a7fde7fbaa773d831 100644 (file)
@@ -191,7 +191,7 @@ int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags)
        }
 
        if (adev->runpm) {
-               dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NEVER_SKIP);
+               dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
                pm_runtime_use_autosuspend(dev->dev);
                pm_runtime_set_autosuspend_delay(dev->dev, 5000);
                pm_runtime_set_active(dev->dev);
index 4a3049841086d826ea8d798606a7af82dd44ffc1..c24cad3c64ed250ee280f68bae38152637b78fee 100644 (file)
@@ -1050,7 +1050,7 @@ void kfd_dec_compute_active(struct kfd_dev *dev);
 /* Check with device cgroup if @kfd device is accessible */
 static inline int kfd_devcgroup_check_permission(struct kfd_dev *kfd)
 {
-#if defined(CONFIG_CGROUP_DEVICE)
+#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
        struct drm_device *ddev = kfd->ddev;
 
        return devcgroup_check_permission(DEVCG_DEV_CHAR, ddev->driver->major,
index 28e651b173abda1f9a6861282ed0ef36f5824b87..7fc15b82fe48afad1d276e416b1d8271d3953631 100644 (file)
@@ -7880,13 +7880,6 @@ static int dm_update_plane_state(struct dc *dc,
                        return -EINVAL;
                }
 
-               if (new_plane_state->crtc_x <= -new_acrtc->max_cursor_width ||
-                       new_plane_state->crtc_y <= -new_acrtc->max_cursor_height) {
-                       DRM_DEBUG_ATOMIC("Bad cursor position %d, %d\n",
-                                                        new_plane_state->crtc_x, new_plane_state->crtc_y);
-                       return -EINVAL;
-               }
-
                return 0;
        }
 
index 82fc3d5b3b2a2749d4a11167cd7bc174b3e098bd..416afb99529d1864031c7d9c5d809aaf2e8c776d 100644 (file)
@@ -1684,6 +1684,8 @@ static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx)
                return;
 
        /* Stall out until the cursor update completes. */
+       if (vupdate_end < vupdate_start)
+               vupdate_end += stream->timing.v_total;
        us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line;
        udelay(us_to_vupdate + us_vupdate);
 }
index 197dca3fc84c2d71d8220d3f797f03e6daf3e351..5232f81c16a57579e672e15894f98c2274428efb 100644 (file)
@@ -311,7 +311,7 @@ static void __exit armada_drm_exit(void)
 }
 module_exit(armada_drm_exit);
 
-MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
 MODULE_DESCRIPTION("Armada DRM Driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:armada-drm");
index dd56996fe9c76a822063ee5fc14fc0eadaa1bde9..d0db1acf11d7319ec722f33202cb91db27971620 100644 (file)
@@ -630,7 +630,7 @@ static struct platform_driver snd_dw_hdmi_driver = {
 
 module_platform_driver(snd_dw_hdmi_driver);
 
-MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
 MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:" DRIVER_NAME);
index 9d89ebf3a749d12cd3543971502e357462a55a47..35b62c5d18b40dfe2a28b2c0fd26ce437605e2fb 100644 (file)
@@ -5499,7 +5499,7 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
 {
        struct drm_dp_mst_port *immediate_upstream_port;
        struct drm_dp_mst_port *fec_port;
-       struct drm_dp_desc desc = { };
+       struct drm_dp_desc desc = { };
        u8 endpoint_fec;
        u8 endpoint_dsc;
 
index eb009d3ab48fa318ddd51dae56784d288ccef17d..6a1f6c802415518319b1393c9d954a82f7af231e 100644 (file)
@@ -569,9 +569,6 @@ ssize_t drm_read(struct file *filp, char __user *buffer,
        struct drm_device *dev = file_priv->minor->dev;
        ssize_t ret;
 
-       if (!access_ok(buffer, count))
-               return -EFAULT;
-
        ret = mutex_lock_interruptible(&file_priv->event_read_lock);
        if (ret)
                return ret;
index 9e41972c4bbc3ddd7c4b3c9083a3575e9f380f08..c2b8d2a953aea7d30e65da7814e3c10331d3410c 100644 (file)
@@ -741,7 +741,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
  *     };
  *
  * Please make sure that you follow all the best practices from
- * ``Documentation/ioctl/botching-up-ioctls.rst``. Note that drm_ioctl()
+ * ``Documentation/process/botching-up-ioctls.rst``. Note that drm_ioctl()
  * automatically zero-extends structures, hence make sure you can add more stuff
  * at the end, i.e. don't put a variable sized array there.
  *
index ca520028b2cb1db39d10d6de223524299737d064..f4e6184d187721bad4c96c05481d18774ed2791d 100644 (file)
 
 #define DEBUG_SCATTER 0
 
-static inline void *drm_vmalloc_dma(unsigned long size)
-{
-#if defined(__powerpc__) && defined(CONFIG_NOT_COHERENT_CACHE)
-       return __vmalloc(size, GFP_KERNEL, pgprot_noncached_wc(PAGE_KERNEL));
-#else
-       return vmalloc_32(size);
-#endif
-}
-
 static void drm_sg_cleanup(struct drm_sg_mem * entry)
 {
        struct page *page;
@@ -126,7 +117,7 @@ int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
                return -ENOMEM;
        }
 
-       entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
+       entry->virtual = vmalloc_32(pages << PAGE_SHIFT);
        if (!entry->virtual) {
                kfree(entry->busaddr);
                kfree(entry->pagelist);
index a8685b2e1803cf91318fd2b50960915710426353..7585d8f68fb94ee7dfd55528132c48c2184de0e8 100644 (file)
@@ -736,7 +736,7 @@ static void __exit etnaviv_exit(void)
 module_exit(etnaviv_exit);
 
 MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
-MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
 MODULE_AUTHOR("Lucas Stach <l.stach@pengutronix.de>");
 MODULE_DESCRIPTION("etnaviv DRM Driver");
 MODULE_LICENSE("GPL v2");
index 648cf0207309219362c7d94df777fc00fece30d1..706af0304ca4c85655b55c7194e445fc48cb8559 100644 (file)
@@ -154,8 +154,8 @@ void etnaviv_core_dump(struct etnaviv_gem_submit *submit)
        file_size += sizeof(*iter.hdr) * n_obj;
 
        /* Allocate the file in vmalloc memory, it's likely to be big */
-       iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY,
-                              PAGE_KERNEL);
+       iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
+                       __GFP_NORETRY);
        if (!iter.start) {
                mutex_unlock(&gpu->mmu_context->lock);
                dev_warn(gpu->dev, "failed to allocate devcoredump file\n");
index b7440f06c5e24e9303b0b6e2cddb997a098d3089..8a4e9c1cbf6c1262d9e68e85760b899e502d46bf 100644 (file)
@@ -2794,7 +2794,8 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
                 * And this range already got effectively checked earlier
                 * when we did the "copy_from_user()" above.
                 */
-               if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list)))
+               if (!user_write_access_begin(user_exec_list,
+                                            count * sizeof(*user_exec_list)))
                        goto end;
 
                for (i = 0; i < args->buffer_count; i++) {
@@ -2808,7 +2809,7 @@ i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
                                        end_user);
                }
 end_user:
-               user_access_end();
+               user_write_access_end();
 end:;
        }
 
index 7ffd7afeb7a5a12e2dcc6d5926b2c103507d0a7c..f80cf6ac20c536dbecf7d849fdeb7452e7eed739 100644 (file)
@@ -598,6 +598,14 @@ static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
                                      GFP_KERNEL |
                                      __GFP_NORETRY |
                                      __GFP_NOWARN);
+               /*
+                * Using __get_user_pages_fast() with a read-only
+                * access is questionable. A read-only page may be
+                * COW-broken, and then this might end up giving
+                * the wrong side of the COW..
+                *
+                * We may or may not care.
+                */
                if (pvec) /* defer to worker if malloc fails */
                        pinned = __get_user_pages_fast(obj->userptr.ptr,
                                                       num_pages,
index 9272bef57092be40012c2be12c815b7e72f1b1fd..debaf7b18ab59cecbd172bc1f4f05123b0579016 100644 (file)
@@ -66,7 +66,7 @@ static void *mock_dmabuf_vmap(struct dma_buf *dma_buf)
 {
        struct mock_dmabuf *mock = to_mock(dma_buf);
 
-       return vm_map_ram(mock->pages, mock->npages, 0, PAGE_KERNEL);
+       return vm_map_ram(mock->pages, mock->npages, 0);
 }
 
 static void mock_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
index cf2c01f17da837235dd8f79e1614cf896e814299..60da28d412d6d0dbf24743f44eb2099611a983ab 100644 (file)
@@ -3388,10 +3388,10 @@ i915_perf_open_ioctl_locked(struct i915_perf *perf,
        /* Similar to perf's kernel.perf_paranoid_cpu sysctl option
         * we check a dev.i915.perf_stream_paranoid sysctl option
         * to determine if it's ok to access system wide OA counters
-        * without CAP_SYS_ADMIN privileges.
+        * without CAP_PERFMON or CAP_SYS_ADMIN privileges.
         */
        if (privileged_op &&
-           i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+           i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to open i915 perf stream\n");
                ret = -EACCES;
                goto err_ctx;
@@ -3584,9 +3584,8 @@ static int read_properties_unlocked(struct i915_perf *perf,
                        } else
                                oa_freq_hz = 0;
 
-                       if (oa_freq_hz > i915_oa_max_sample_rate &&
-                           !capable(CAP_SYS_ADMIN)) {
-                               DRM_DEBUG("OA exponent would exceed the max sampling frequency (sysctl dev.i915.oa_max_sample_rate) %uHz without root privileges\n",
+                       if (oa_freq_hz > i915_oa_max_sample_rate && !perfmon_capable()) {
+                               DRM_DEBUG("OA exponent would exceed the max sampling frequency (sysctl dev.i915.oa_max_sample_rate) %uHz without CAP_PERFMON or CAP_SYS_ADMIN privileges\n",
                                          i915_oa_max_sample_rate);
                                return -EACCES;
                        }
@@ -4007,7 +4006,7 @@ int i915_perf_add_config_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
 
-       if (i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+       if (i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to add i915 OA config\n");
                return -EACCES;
        }
@@ -4154,7 +4153,7 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data,
                return -ENOTSUPP;
        }
 
-       if (i915_perf_stream_paranoid && !capable(CAP_SYS_ADMIN)) {
+       if (i915_perf_stream_paranoid && !perfmon_capable()) {
                DRM_DEBUG("Insufficient privileges to remove i915 OA config\n");
                return -EACCES;
        }
index ad719c9602af9abfa1363acfb774098c22aee8c0..9cb2d7548daadff98b1da0d466c06f314e9f2694 100644 (file)
@@ -549,7 +549,7 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
         * becaue the HDA driver may require us to enable the audio power
         * domain during system suspend.
         */
-       dev_pm_set_driver_flags(kdev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(kdev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        pm_runtime_set_autosuspend_delay(kdev, 10000); /* 10s */
        pm_runtime_mark_last_busy(kdev);
index 1754c054706906f6a419923b3e239f27aa8a4328..548cc25ea4abed9565f1eebed9af9aa92b908a58 100644 (file)
@@ -328,8 +328,8 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
        if (!drm_atomic_crtc_needs_modeset(state))
                return 0;
 
-       if (state->mode.hdisplay > priv->soc_info->max_height ||
-           state->mode.vdisplay > priv->soc_info->max_width)
+       if (state->mode.hdisplay > priv->soc_info->max_width ||
+           state->mode.vdisplay > priv->soc_info->max_height)
                return -EINVAL;
 
        rate = clk_round_rate(priv->pix_clk,
@@ -474,7 +474,7 @@ static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,
 
 static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
 {
-       struct ingenic_drm *priv = arg;
+       struct ingenic_drm *priv = drm_device_get_priv(arg);
        unsigned int state;
 
        regmap_read(priv->map, JZ_REG_LCD_STATE, &state);
index 211f5de99a441b020e8001be040d0238c5dd2ddf..9aba2910d83adfac0479ad4fc3e6e4b16a080a91 100644 (file)
@@ -170,7 +170,7 @@ struct dpu_global_state
  *
  * Main debugfs documentation is located at,
  *
- * Documentation/filesystems/debugfs.txt
+ * Documentation/filesystems/debugfs.rst
  *
  * @dpu_debugfs_setup_regset32: Initialize data for dpu_debugfs_create_regset32
  * @dpu_debugfs_create_regset32: Create 32-bit register dump file
index 58176db85952cb990f3af9f511c91c007e3a7687..372962358a18190d321ca685dd59e4dd8efd5246 100644 (file)
@@ -158,7 +158,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
        }
 
        if (radeon_is_px(dev)) {
-               dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NEVER_SKIP);
+               dev_pm_set_driver_flags(dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
                pm_runtime_use_autosuspend(dev->dev);
                pm_runtime_set_autosuspend_delay(dev->dev, 5000);
                pm_runtime_set_active(dev->dev);
index 4c62f900bf7e8f6ae246d5a8b8aacf3b7d65a719..288ae9f63588ca3727a15a4a00581ef68e89585e 100644 (file)
@@ -324,6 +324,16 @@ config SENSORS_FAM15H_POWER
          This driver can also be built as a module. If so, the module
          will be called fam15h_power.
 
+config SENSORS_AMD_ENERGY
+       tristate "AMD RAPL MSR based Energy driver"
+       depends on X86
+       help
+         If you say yes here you get support for core and package energy
+         sensors, based on RAPL MSR for AMD family 17h and above CPUs.
+
+         This driver can also be built as a module. If so, the module
+         will be called as amd_energy.
+
 config SENSORS_APPLESMC
        tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
        depends on INPUT && X86
@@ -404,6 +414,31 @@ config SENSORS_ATXP1
          This driver can also be built as a module. If so, the module
          will be called atxp1.
 
+config SENSORS_BT1_PVT
+       tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
+       depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+       help
+         If you say yes here you get support for Baikal-T1 PVT sensor
+         embedded into the SoC.
+
+         This driver can also be built as a module. If so, the module will be
+         called bt1-pvt.
+
+config SENSORS_BT1_PVT_ALARMS
+       bool "Enable Baikal-T1 PVT sensor alarms"
+       depends on SENSORS_BT1_PVT
+       help
+         Baikal-T1 PVT IP-block provides threshold registers for each
+         supported sensor. But the corresponding interrupts might be
+         generated by the thresholds comparator only in synchronization with
+         a data conversion. Additionally there is only one sensor data can
+         be converted at a time. All of these makes the interface impossible
+         to be used for the hwmon alarms implementation without periodic
+         switch between the PVT sensors. By default the data conversion is
+         performed on demand from the user-space. If this config is enabled
+         the data conversion will be periodically performed and the data will be
+         saved in the internal driver cache.
+
 config SENSORS_DRIVETEMP
        tristate "Hard disk drives with temperature sensors"
        depends on SCSI && ATA
@@ -523,6 +558,15 @@ config SENSORS_F75375S
          This driver can also be built as a module. If so, the module
          will be called f75375s.
 
+config SENSORS_GSC
+       tristate "Gateworks System Controller ADC"
+       depends on MFD_GATEWORKS_GSC
+       help
+         Support for the Gateworks System Controller A/D converters.
+
+         To compile this driver as a module, choose M here:
+         the module will be called gsc-hwmon.
+
 config SENSORS_MC13783_ADC
         tristate "Freescale MC13783/MC13892 ADC"
         depends on MFD_MC13XXX
@@ -1198,10 +1242,11 @@ config SENSORS_LM90
        help
          If you say yes here you get support for National Semiconductor LM90,
          LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
-         Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
-         MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
-         Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, and
-         Texas Instruments TMP451 sensor chips.
+         Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
+         MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
+         ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
+         Philips SA56004, GMT G781, and Texas Instruments TMP451
+         sensor chips.
 
          This driver can also be built as a module. If so, the module
          will be called lm90.
@@ -1340,10 +1385,12 @@ config SENSORS_NCT7802
 
 config SENSORS_NCT7904
        tristate "Nuvoton NCT7904"
-       depends on I2C
+       depends on I2C && WATCHDOG
+       select WATCHDOG_CORE
        help
          If you say yes here you get support for the Nuvoton NCT7904
-         hardware monitoring chip, including manual fan speed control.
+         hardware monitoring chip, including manual fan speed control
+         and support for the integrated watchdog.
 
          This driver can also be built as a module. If so, the module
          will be called nct7904.
index b0b9c8e57176299850ed861b06bff5fbbbdd4534..3e32c21f5efe30dbf8fcb649657867d8a6c1a80f 100644 (file)
@@ -45,6 +45,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
 obj-$(CONFIG_SENSORS_ADT7462)  += adt7462.o
 obj-$(CONFIG_SENSORS_ADT7470)  += adt7470.o
 obj-$(CONFIG_SENSORS_ADT7475)  += adt7475.o
+obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
 obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
 obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
 obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
@@ -53,6 +54,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)   += aspeed-pwm-tacho.o
 obj-$(CONFIG_SENSORS_ATXP1)    += atxp1.o
 obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
+obj-$(CONFIG_SENSORS_BT1_PVT)  += bt1-pvt.o
 obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
 obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
 obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
@@ -74,6 +76,7 @@ obj-$(CONFIG_SENSORS_G760A)   += g760a.o
 obj-$(CONFIG_SENSORS_G762)     += g762.o
 obj-$(CONFIG_SENSORS_GL518SM)  += gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)  += gl520sm.o
+obj-$(CONFIG_SENSORS_GSC)      += gsc-hwmon.o
 obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
 obj-$(CONFIG_SENSORS_HIH6130)  += hih6130.o
 obj-$(CONFIG_SENSORS_ULTRA45)  += ultra45_env.o
index c7010b91bc1335330ad054cd250c787236447433..5a839cc2ed1c3ae8861acb17e589d9d31d3476ab 100644 (file)
@@ -716,7 +716,6 @@ static struct i2c_driver adt7411_driver = {
 
 module_i2c_driver(adt7411_driver);
 
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and "
-       "Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Sascha Hauer, Wolfram Sang <kernel@pengutronix.de>");
 MODULE_DESCRIPTION("ADT7411 driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/amd_energy.c b/drivers/hwmon/amd_energy.c
new file mode 100644 (file)
index 0000000..e95b742
--- /dev/null
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2020 Advanced Micro Devices, Inc.
+ */
+#include <asm/cpu_device_id.h>
+
+#include <linux/bits.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/processor.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/topology.h>
+#include <linux/types.h>
+
+#define DRVNAME                        "amd_energy"
+
+#define ENERGY_PWR_UNIT_MSR    0xC0010299
+#define ENERGY_CORE_MSR                0xC001029A
+#define ENERGY_PKG_MSR         0xC001029B
+
+#define AMD_ENERGY_UNIT_MASK   0x01F00
+#define AMD_ENERGY_MASK                0xFFFFFFFF
+
+struct sensor_accumulator {
+       u64 energy_ctr;
+       u64 prev_value;
+       char label[10];
+};
+
+struct amd_energy_data {
+       struct hwmon_channel_info energy_info;
+       const struct hwmon_channel_info *info[2];
+       struct hwmon_chip_info chip;
+       struct task_struct *wrap_accumulate;
+       /* Lock around the accumulator */
+       struct mutex lock;
+       /* An accumulator for each core and socket */
+       struct sensor_accumulator *accums;
+       /* Energy Status Units */
+       u64 energy_units;
+       int nr_cpus;
+       int nr_socks;
+       int core_id;
+};
+
+static int amd_energy_read_labels(struct device *dev,
+                                 enum hwmon_sensor_types type,
+                                 u32 attr, int channel,
+                                 const char **str)
+{
+       struct amd_energy_data *data = dev_get_drvdata(dev);
+
+       *str = data->accums[channel].label;
+       return 0;
+}
+
+static void get_energy_units(struct amd_energy_data *data)
+{
+       u64 rapl_units;
+
+       rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units);
+       data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8;
+}
+
+static void accumulate_socket_delta(struct amd_energy_data *data,
+                                   int sock, int cpu)
+{
+       struct sensor_accumulator *s_accum;
+       u64 input;
+
+       mutex_lock(&data->lock);
+       rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
+       input &= AMD_ENERGY_MASK;
+
+       s_accum = &data->accums[data->nr_cpus + sock];
+       if (input >= s_accum->prev_value)
+               s_accum->energy_ctr +=
+                       input - s_accum->prev_value;
+       else
+               s_accum->energy_ctr += UINT_MAX -
+                       s_accum->prev_value + input;
+
+       s_accum->prev_value = input;
+       mutex_unlock(&data->lock);
+}
+
+static void accumulate_core_delta(struct amd_energy_data *data)
+{
+       struct sensor_accumulator *c_accum;
+       u64 input;
+       int cpu;
+
+       mutex_lock(&data->lock);
+       if (data->core_id >= data->nr_cpus)
+               data->core_id = 0;
+
+       cpu = data->core_id;
+
+       if (!cpu_online(cpu))
+               goto out;
+
+       rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
+       input &= AMD_ENERGY_MASK;
+
+       c_accum = &data->accums[cpu];
+
+       if (input >= c_accum->prev_value)
+               c_accum->energy_ctr +=
+                       input - c_accum->prev_value;
+       else
+               c_accum->energy_ctr += UINT_MAX -
+                       c_accum->prev_value + input;
+
+       c_accum->prev_value = input;
+
+out:
+       data->core_id++;
+       mutex_unlock(&data->lock);
+}
+
+static void read_accumulate(struct amd_energy_data *data)
+{
+       int sock;
+
+       for (sock = 0; sock < data->nr_socks; sock++) {
+               int cpu;
+
+               cpu = cpumask_first_and(cpu_online_mask,
+                                       cpumask_of_node(sock));
+
+               accumulate_socket_delta(data, sock, cpu);
+       }
+
+       accumulate_core_delta(data);
+}
+
+static void amd_add_delta(struct amd_energy_data *data, int ch,
+                         int cpu, long *val, bool is_core)
+{
+       struct sensor_accumulator *s_accum, *c_accum;
+       u64 input;
+
+       mutex_lock(&data->lock);
+       if (!is_core) {
+               rdmsrl_safe_on_cpu(cpu, ENERGY_PKG_MSR, &input);
+               input &= AMD_ENERGY_MASK;
+
+               s_accum = &data->accums[ch];
+               if (input >= s_accum->prev_value)
+                       input += s_accum->energy_ctr -
+                                 s_accum->prev_value;
+               else
+                       input += UINT_MAX - s_accum->prev_value +
+                                 s_accum->energy_ctr;
+       } else {
+               rdmsrl_safe_on_cpu(cpu, ENERGY_CORE_MSR, &input);
+               input &= AMD_ENERGY_MASK;
+
+               c_accum = &data->accums[ch];
+               if (input >= c_accum->prev_value)
+                       input += c_accum->energy_ctr -
+                                c_accum->prev_value;
+               else
+                       input += UINT_MAX - c_accum->prev_value +
+                                c_accum->energy_ctr;
+       }
+
+       /* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */
+       *val = div64_ul(input * 1000000UL, BIT(data->energy_units));
+
+       mutex_unlock(&data->lock);
+}
+
+static int amd_energy_read(struct device *dev,
+                          enum hwmon_sensor_types type,
+                          u32 attr, int channel, long *val)
+{
+       struct amd_energy_data *data = dev_get_drvdata(dev);
+       int cpu;
+
+       if (channel >= data->nr_cpus) {
+               cpu = cpumask_first_and(cpu_online_mask,
+                                       cpumask_of_node
+                                       (channel - data->nr_cpus));
+               amd_add_delta(data, channel, cpu, val, false);
+       } else {
+               cpu = channel;
+               if (!cpu_online(cpu))
+                       return -ENODEV;
+
+               amd_add_delta(data, channel, cpu, val, true);
+       }
+
+       return 0;
+}
+
+static umode_t amd_energy_is_visible(const void *_data,
+                                    enum hwmon_sensor_types type,
+                                    u32 attr, int channel)
+{
+       return 0444;
+}
+
+static int energy_accumulator(void *p)
+{
+       struct amd_energy_data *data = (struct amd_energy_data *)p;
+
+       while (!kthread_should_stop()) {
+               /*
+                * Ignoring the conditions such as
+                * cpu being offline or rdmsr failure
+                */
+               read_accumulate(data);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (kthread_should_stop())
+                       break;
+
+               /*
+                * On a 240W system, with default resolution the
+                * Socket Energy status register may wrap around in
+                * 2^32*15.3 e-6/240 = 273.8041 secs (~4.5 mins)
+                *
+                * let us accumulate for every 100secs
+                */
+               schedule_timeout(msecs_to_jiffies(100000));
+       }
+       return 0;
+}
+
+static const struct hwmon_ops amd_energy_ops = {
+       .is_visible = amd_energy_is_visible,
+       .read = amd_energy_read,
+       .read_string = amd_energy_read_labels,
+};
+
+static int amd_create_sensor(struct device *dev,
+                            struct amd_energy_data *data,
+                            u8 type, u32 config)
+{
+       struct hwmon_channel_info *info = &data->energy_info;
+       struct sensor_accumulator *accums;
+       int i, num_siblings, cpus, sockets;
+       u32 *s_config;
+
+       /* Identify the number of siblings per core */
+       num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
+
+       sockets = num_possible_nodes();
+
+       /*
+        * Energy counter register is accessed at core level.
+        * Hence, filterout the siblings.
+        */
+       cpus = num_present_cpus() / num_siblings;
+
+       s_config = devm_kcalloc(dev, cpus + sockets,
+                               sizeof(u32), GFP_KERNEL);
+       if (!s_config)
+               return -ENOMEM;
+
+       accums = devm_kcalloc(dev, cpus + sockets,
+                             sizeof(struct sensor_accumulator),
+                             GFP_KERNEL);
+       if (!accums)
+               return -ENOMEM;
+
+       info->type = type;
+       info->config = s_config;
+
+       data->nr_cpus = cpus;
+       data->nr_socks = sockets;
+       data->accums = accums;
+
+       for (i = 0; i < cpus + sockets; i++) {
+               s_config[i] = config;
+               if (i < cpus)
+                       scnprintf(accums[i].label, 10,
+                                 "Ecore%03u", i);
+               else
+                       scnprintf(accums[i].label, 10,
+                                 "Esocket%u", (i - cpus));
+       }
+
+       return 0;
+}
+
+static int amd_energy_probe(struct platform_device *pdev)
+{
+       struct device *hwmon_dev;
+       struct amd_energy_data *data;
+       struct device *dev = &pdev->dev;
+
+       data = devm_kzalloc(dev,
+                           sizeof(struct amd_energy_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->chip.ops = &amd_energy_ops;
+       data->chip.info = data->info;
+
+       dev_set_drvdata(dev, data);
+       /* Populate per-core energy reporting */
+       data->info[0] = &data->energy_info;
+       amd_create_sensor(dev, data, hwmon_energy,
+                         HWMON_E_INPUT | HWMON_E_LABEL);
+
+       mutex_init(&data->lock);
+       get_energy_units(data);
+
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME,
+                                                        data,
+                                                        &data->chip,
+                                                        NULL);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+
+       data->wrap_accumulate = kthread_run(energy_accumulator, data,
+                                           "%s", dev_name(hwmon_dev));
+       if (IS_ERR(data->wrap_accumulate))
+               return PTR_ERR(data->wrap_accumulate);
+
+       return PTR_ERR_OR_ZERO(data->wrap_accumulate);
+}
+
+static int amd_energy_remove(struct platform_device *pdev)
+{
+       struct amd_energy_data *data = dev_get_drvdata(&pdev->dev);
+
+       if (data && data->wrap_accumulate)
+               kthread_stop(data->wrap_accumulate);
+
+       return 0;
+}
+
+static const struct platform_device_id amd_energy_ids[] = {
+       { .name = DRVNAME, },
+       {}
+};
+MODULE_DEVICE_TABLE(platform, amd_energy_ids);
+
+static struct platform_driver amd_energy_driver = {
+       .probe = amd_energy_probe,
+       .remove = amd_energy_remove,
+       .id_table = amd_energy_ids,
+       .driver = {
+               .name = DRVNAME,
+       },
+};
+
+static struct platform_device *amd_energy_platdev;
+
+static const struct x86_cpu_id cpu_ids[] __initconst = {
+       X86_MATCH_VENDOR_FAM(AMD, 0x17, NULL),
+       {}
+};
+MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
+
+static int __init amd_energy_init(void)
+{
+       int ret;
+
+       if (!x86_match_cpu(cpu_ids))
+               return -ENODEV;
+
+       ret = platform_driver_register(&amd_energy_driver);
+       if (ret)
+               return ret;
+
+       amd_energy_platdev = platform_device_alloc(DRVNAME, 0);
+       if (!amd_energy_platdev) {
+               platform_driver_unregister(&amd_energy_driver);
+               return -ENOMEM;
+       }
+
+       ret = platform_device_add(amd_energy_platdev);
+       if (ret) {
+               platform_device_put(amd_energy_platdev);
+               platform_driver_unregister(&amd_energy_driver);
+               return ret;
+       }
+
+       return ret;
+}
+
+static void __exit amd_energy_exit(void)
+{
+       platform_device_unregister(amd_energy_platdev);
+       platform_driver_unregister(&amd_energy_driver);
+}
+
+module_init(amd_energy_init);
+module_exit(amd_energy_exit);
+
+MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface");
+MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>");
+MODULE_LICENSE("GPL");
index ec93b8d673f5cbce6ca4c7b880e1a61bcb5c7e4d..31661840931577e54cc75e654ec6b4f86af630c0 100644 (file)
@@ -156,14 +156,19 @@ static struct workqueue_struct *applesmc_led_wq;
  */
 static int wait_read(void)
 {
+       unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ) / USEC_PER_SEC;
        u8 status;
        int us;
+
        for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
-               udelay(us);
+               usleep_range(us, us * 16);
                status = inb(APPLESMC_CMD_PORT);
                /* read: wait for smc to settle */
                if (status & 0x01)
                        return 0;
+               /* timeout: give up */
+               if (time_after(jiffies, end))
+                       break;
        }
 
        pr_warn("wait_read() fail: 0x%02x\n", status);
@@ -178,10 +183,11 @@ static int send_byte(u8 cmd, u16 port)
 {
        u8 status;
        int us;
+       unsigned long end = jiffies + (APPLESMC_MAX_WAIT * HZ) / USEC_PER_SEC;
 
        outb(cmd, port);
        for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
-               udelay(us);
+               usleep_range(us, us * 16);
                status = inb(APPLESMC_CMD_PORT);
                /* write: wait for smc to settle */
                if (status & 0x02)
@@ -190,7 +196,7 @@ static int send_byte(u8 cmd, u16 port)
                if (status & 0x04)
                        return 0;
                /* timeout: give up */
-               if (us << 1 == APPLESMC_MAX_WAIT)
+               if (time_after(jiffies, end))
                        break;
                /* busy: long wait and resend */
                udelay(APPLESMC_RETRY_WAIT);
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
new file mode 100644 (file)
index 0000000..1a9772f
--- /dev/null
@@ -0,0 +1,1146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Authors:
+ *   Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>
+ *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/seqlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include "bt1-pvt.h"
+
+/*
+ * For the sake of the code simplification we created the sensors info table
+ * with the sensor names, activation modes, threshold registers base address
+ * and the thresholds bit fields.
+ */
+static const struct pvt_sensor_info pvt_info[] = {
+       PVT_SENSOR_INFO(0, "CPU Core Temperature", hwmon_temp, TEMP, TTHRES),
+       PVT_SENSOR_INFO(0, "CPU Core Voltage", hwmon_in, VOLT, VTHRES),
+       PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES),
+       PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES),
+       PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES),
+};
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * to PVT data and vice-versa are following:
+ * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
+ *     1.7204e2,
+ * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
+ *     3.1020e-1*(N^1) - 4.838e1,
+ * where T = [-48.380, 147.438]C and N = [0, 1023].
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what they look like after
+ * the alterations:
+ * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
+ *     17204e2) / 1e4,
+ * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
+ *     48380,
+ * where T = [-48380, 147438] mC and N = [0, 1023].
+ */
+static const struct pvt_poly poly_temp_to_N = {
+       .total_divider = 10000,
+       .terms = {
+               {4, 18322, 10000, 10000},
+               {3, 2343, 10000, 10},
+               {2, 87018, 10000, 10},
+               {1, 39269, 1000, 1},
+               {0, 1720400, 1, 1}
+       }
+};
+
+static const struct pvt_poly poly_N_to_temp = {
+       .total_divider = 1,
+       .terms = {
+               {4, -16743, 1000, 1},
+               {3, 81542, 1000, 1},
+               {2, -182010, 1000, 1},
+               {1, 310200, 1000, 1},
+               {0, -48380, 1, 1}
+       }
+};
+
+/*
+ * Similar alterations are performed for the voltage conversion equations.
+ * The original formulae are:
+ * N = 1.8658e3*V - 1.1572e3,
+ * V = (N + 1.1572e3) / 1.8658e3,
+ * where V = [0.620, 1.168] V and N = [0, 1023].
+ * After the optimization they looks as follows:
+ * N = (18658e-3*V - 11572) / 10,
+ * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
+ */
+static const struct pvt_poly poly_volt_to_N = {
+       .total_divider = 10,
+       .terms = {
+               {1, 18658, 1000, 1},
+               {0, -11572, 1, 1}
+       }
+};
+
+static const struct pvt_poly poly_N_to_volt = {
+       .total_divider = 10,
+       .terms = {
+               {1, 100000, 18658, 1},
+               {0, 115720000, 1, 18658}
+       }
+};
+
+/*
+ * Here is the polynomial calculation function, which performs the
+ * redistributed terms calculations. It's pretty straightforward. We walk
+ * over each degree term up to the free one, and perform the redistributed
+ * multiplication of the term coefficient, its divider (as for the rationale
+ * fraction representation), data power and the rational fraction divider
+ * leftover. Then all of this is collected in a total sum variable, which
+ * value is normalized by the total divider before being returned.
+ */
+static long pvt_calc_poly(const struct pvt_poly *poly, long data)
+{
+       const struct pvt_poly_term *term = poly->terms;
+       long tmp, ret = 0;
+       int deg;
+
+       do {
+               tmp = term->coef;
+               for (deg = 0; deg < term->deg; ++deg)
+                       tmp = mult_frac(tmp, data, term->divider);
+               ret += tmp / term->divider_leftover;
+       } while ((term++)->deg);
+
+       return ret / poly->total_divider;
+}
+
+static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
+{
+       u32 old;
+
+       old = readl_relaxed(reg);
+       writel((old & ~mask) | (data & mask), reg);
+
+       return old & mask;
+}
+
+/*
+ * Baikal-T1 PVT mode can be updated only when the controller is disabled.
+ * So first we disable it, then set the new mode together with the controller
+ * getting back enabled. The same concerns the temperature trim and
+ * measurements timeout. If it is necessary the interface mutex is supposed
+ * to be locked at the time the operations are performed.
+ */
+static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
+{
+       u32 old;
+
+       mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
+
+       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
+                  mode | old);
+}
+
+static inline u32 pvt_calc_trim(long temp)
+{
+       temp = clamp_val(temp, 0, PVT_TRIM_TEMP);
+
+       return DIV_ROUND_UP(temp, PVT_TRIM_STEP);
+}
+
+static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
+{
+       u32 old;
+
+       trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
+
+       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
+                  trim | old);
+}
+
+static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
+{
+       u32 old;
+
+       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       writel(tout, pvt->regs + PVT_TTIMEOUT);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
+}
+
+/*
+ * This driver can optionally provide the hwmon alarms for each sensor the PVT
+ * controller supports. The alarms functionality is made compile-time
+ * configurable due to the hardware interface implementation peculiarity
+ * described further in this comment. So in case if alarms are unnecessary in
+ * your system design it's recommended to have them disabled to prevent the PVT
+ * IRQs being periodically raised to get the data cache/alarms status up to
+ * date.
+ *
+ * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor,
+ * but is equipped with a dedicated control wrapper. It exposes the PVT
+ * sub-block registers space via the APB3 bus. In addition the wrapper provides
+ * a common interrupt vector of the sensors conversion completion events and
+ * threshold value alarms. Alas the wrapper interface hasn't been fully thought
+ * through. There is only one sensor can be activated at a time, for which the
+ * thresholds comparator is enabled right after the data conversion is
+ * completed. Due to this if alarms need to be implemented for all available
+ * sensors we can't just set the thresholds and enable the interrupts. We need
+ * to enable the sensors one after another and let the controller to detect
+ * the alarms by itself at each conversion. This also makes pointless to handle
+ * the alarms interrupts, since in occasion they happen synchronously with
+ * data conversion completion. The best driver design would be to have the
+ * completion interrupts enabled only and keep the converted value in the
+ * driver data cache. This solution is implemented if hwmon alarms are enabled
+ * in this driver. In case if the alarms are disabled, the conversion is
+ * performed on demand at the time a sensors input file is read.
+ */
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+#define pvt_hard_isr NULL
+
+static irqreturn_t pvt_soft_isr(int irq, void *data)
+{
+       const struct pvt_sensor_info *info;
+       struct pvt_hwmon *pvt = data;
+       struct pvt_cache *cache;
+       u32 val, thres_sts, old;
+
+       /*
+        * DVALID bit will be cleared by reading the data. We need to save the
+        * status before the next conversion happens. Threshold events will be
+        * handled a bit later.
+        */
+       thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
+
+       /*
+        * Then lets recharge the PVT interface with the next sampling mode.
+        * Lock the interface mutex to serialize trim, timeouts and alarm
+        * thresholds settings.
+        */
+       cache = &pvt->cache[pvt->sensor];
+       info = &pvt_info[pvt->sensor];
+       pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ?
+                     PVT_SENSOR_FIRST : (pvt->sensor + 1);
+
+       /*
+        * For some reason we have to mask the interrupt before changing the
+        * mode, otherwise sometimes the temperature mode doesn't get
+        * activated even though the actual mode in the ctrl register
+        * corresponds to one. Then we read the data. By doing so we also
+        * recharge the data conversion. After this the mode corresponding
+        * to the next sensor in the row is set. Finally we enable the
+        * interrupts back.
+        */
+       mutex_lock(&pvt->iface_mtx);
+
+       old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+                        PVT_INTR_DVALID);
+
+       val = readl(pvt->regs + PVT_DATA);
+
+       pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
+
+       mutex_unlock(&pvt->iface_mtx);
+
+       /*
+        * We can now update the data cache with data just retrieved from the
+        * sensor. Lock write-seqlock to make sure the reader has a coherent
+        * data.
+        */
+       write_seqlock(&cache->data_seqlock);
+
+       cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val);
+
+       write_sequnlock(&cache->data_seqlock);
+
+       /*
+        * While PVT core is doing the next mode data conversion, we'll check
+        * whether the alarms were triggered for the current sensor. Note that
+        * according to the documentation only one threshold IRQ status can be
+        * set at a time, that's why if-else statement is utilized.
+        */
+       if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) {
+               WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo);
+               hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm,
+                                  info->channel);
+       } else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) {
+               WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi);
+               hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm,
+                                  info->channel);
+       }
+
+       return IRQ_HANDLED;
+}
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+       return 0644;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+       return 0444;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                        long *val)
+{
+       struct pvt_cache *cache = &pvt->cache[type];
+       unsigned int seq;
+       u32 data;
+
+       do {
+               seq = read_seqbegin(&cache->data_seqlock);
+               data = cache->data;
+       } while (read_seqretry(&cache->data_seqlock, seq));
+
+       if (type == PVT_TEMP)
+               *val = pvt_calc_poly(&poly_N_to_temp, data);
+       else
+               *val = pvt_calc_poly(&poly_N_to_volt, data);
+
+       return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                         bool is_low, long *val)
+{
+       u32 data;
+
+       /* No need in serialization, since it is just read from MMIO. */
+       data = readl(pvt->regs + pvt_info[type].thres_base);
+
+       if (is_low)
+               data = FIELD_GET(PVT_THRES_LO_MASK, data);
+       else
+               data = FIELD_GET(PVT_THRES_HI_MASK, data);
+
+       if (type == PVT_TEMP)
+               *val = pvt_calc_poly(&poly_N_to_temp, data);
+       else
+               *val = pvt_calc_poly(&poly_N_to_volt, data);
+
+       return 0;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                          bool is_low, long val)
+{
+       u32 data, limit, mask;
+       int ret;
+
+       if (type == PVT_TEMP) {
+               val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
+               data = pvt_calc_poly(&poly_temp_to_N, val);
+       } else {
+               val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
+               data = pvt_calc_poly(&poly_volt_to_N, val);
+       }
+
+       /* Serialize limit update, since a part of the register is changed. */
+       ret = mutex_lock_interruptible(&pvt->iface_mtx);
+       if (ret)
+               return ret;
+
+       /* Make sure the upper and lower ranges don't intersect. */
+       limit = readl(pvt->regs + pvt_info[type].thres_base);
+       if (is_low) {
+               limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
+               data = clamp_val(data, PVT_DATA_MIN, limit);
+               data = FIELD_PREP(PVT_THRES_LO_MASK, data);
+               mask = PVT_THRES_LO_MASK;
+       } else {
+               limit = FIELD_GET(PVT_THRES_LO_MASK, limit);
+               data = clamp_val(data, limit, PVT_DATA_MAX);
+               data = FIELD_PREP(PVT_THRES_HI_MASK, data);
+               mask = PVT_THRES_HI_MASK;
+       }
+
+       pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
+
+       mutex_unlock(&pvt->iface_mtx);
+
+       return 0;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                         bool is_low, long *val)
+{
+       if (is_low)
+               *val = !!READ_ONCE(pvt->cache[type].thres_sts_lo);
+       else
+               *val = !!READ_ONCE(pvt->cache[type].thres_sts_hi);
+
+       return 0;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+       HWMON_CHANNEL_INFO(chip,
+                          HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
+                          HWMON_T_MIN | HWMON_T_MIN_ALARM |
+                          HWMON_T_MAX | HWMON_T_MAX_ALARM |
+                          HWMON_T_OFFSET),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL |
+                          HWMON_I_MIN | HWMON_I_MIN_ALARM |
+                          HWMON_I_MAX | HWMON_I_MAX_ALARM,
+                          HWMON_I_INPUT | HWMON_I_LABEL |
+                          HWMON_I_MIN | HWMON_I_MIN_ALARM |
+                          HWMON_I_MAX | HWMON_I_MAX_ALARM,
+                          HWMON_I_INPUT | HWMON_I_LABEL |
+                          HWMON_I_MIN | HWMON_I_MIN_ALARM |
+                          HWMON_I_MAX | HWMON_I_MAX_ALARM,
+                          HWMON_I_INPUT | HWMON_I_LABEL |
+                          HWMON_I_MIN | HWMON_I_MIN_ALARM |
+                          HWMON_I_MAX | HWMON_I_MAX_ALARM),
+       NULL
+};
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static irqreturn_t pvt_hard_isr(int irq, void *data)
+{
+       struct pvt_hwmon *pvt = data;
+       struct pvt_cache *cache;
+       u32 val;
+
+       /*
+        * Mask the DVALID interrupt so after exiting from the handler a
+        * repeated conversion wouldn't happen.
+        */
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+                  PVT_INTR_DVALID);
+
+       /*
+        * Nothing special for alarm-less driver. Just read the data, update
+        * the cache and notify a waiter of this event.
+        */
+       val = readl(pvt->regs + PVT_DATA);
+       if (!(val & PVT_DATA_VALID)) {
+               dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
+               return IRQ_HANDLED;
+       }
+
+       cache = &pvt->cache[pvt->sensor];
+
+       WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val));
+
+       complete(&cache->conversion);
+
+       return IRQ_HANDLED;
+}
+
+#define pvt_soft_isr NULL
+
+inline umode_t pvt_limit_is_visible(enum pvt_sensor_type type)
+{
+       return 0;
+}
+
+inline umode_t pvt_alarm_is_visible(enum pvt_sensor_type type)
+{
+       return 0;
+}
+
+static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                        long *val)
+{
+       struct pvt_cache *cache = &pvt->cache[type];
+       u32 data;
+       int ret;
+
+       /*
+        * Lock PVT conversion interface until data cache is updated. The
+        * data read procedure is following: set the requested PVT sensor
+        * mode, enable IRQ and conversion, wait until conversion is finished,
+        * then disable conversion and IRQ, and read the cached data.
+        */
+       ret = mutex_lock_interruptible(&pvt->iface_mtx);
+       if (ret)
+               return ret;
+
+       pvt->sensor = type;
+       pvt_set_mode(pvt, pvt_info[type].mode);
+
+       /*
+        * Unmask the DVALID interrupt and enable the sensors conversions.
+        * Do the reverse procedure when conversion is done.
+        */
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+
+       wait_for_completion(&cache->conversion);
+
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+                  PVT_INTR_DVALID);
+
+       data = READ_ONCE(cache->data);
+
+       mutex_unlock(&pvt->iface_mtx);
+
+       if (type == PVT_TEMP)
+               *val = pvt_calc_poly(&poly_N_to_temp, data);
+       else
+               *val = pvt_calc_poly(&poly_N_to_volt, data);
+
+       return 0;
+}
+
+static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                         bool is_low, long *val)
+{
+       return -EOPNOTSUPP;
+}
+
+static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                          bool is_low, long val)
+{
+       return -EOPNOTSUPP;
+}
+
+static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
+                         bool is_low, long *val)
+{
+       return -EOPNOTSUPP;
+}
+
+static const struct hwmon_channel_info *pvt_channel_info[] = {
+       HWMON_CHANNEL_INFO(chip,
+                          HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_TYPE | HWMON_T_LABEL |
+                          HWMON_T_OFFSET),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL),
+       NULL
+};
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static inline bool pvt_hwmon_channel_is_valid(enum hwmon_sensor_types type,
+                                             int ch)
+{
+       switch (type) {
+       case hwmon_temp:
+               if (ch < 0 || ch >= PVT_TEMP_CHS)
+                       return false;
+               break;
+       case hwmon_in:
+               if (ch < 0 || ch >= PVT_VOLT_CHS)
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       /* The rest of the types are independent from the channel number. */
+       return true;
+}
+
+static umode_t pvt_hwmon_is_visible(const void *data,
+                                   enum hwmon_sensor_types type,
+                                   u32 attr, int ch)
+{
+       if (!pvt_hwmon_channel_is_valid(type, ch))
+               return 0;
+
+       switch (type) {
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+                       return 0644;
+               }
+               break;
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_input:
+               case hwmon_temp_type:
+               case hwmon_temp_label:
+                       return 0444;
+               case hwmon_temp_min:
+               case hwmon_temp_max:
+                       return pvt_limit_is_visible(ch);
+               case hwmon_temp_min_alarm:
+               case hwmon_temp_max_alarm:
+                       return pvt_alarm_is_visible(ch);
+               case hwmon_temp_offset:
+                       return 0644;
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+               case hwmon_in_label:
+                       return 0444;
+               case hwmon_in_min:
+               case hwmon_in_max:
+                       return pvt_limit_is_visible(PVT_VOLT + ch);
+               case hwmon_in_min_alarm:
+               case hwmon_in_max_alarm:
+                       return pvt_alarm_is_visible(PVT_VOLT + ch);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int pvt_read_trim(struct pvt_hwmon *pvt, long *val)
+{
+       u32 data;
+
+       data = readl(pvt->regs + PVT_CTRL);
+       *val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP;
+
+       return 0;
+}
+
+static int pvt_write_trim(struct pvt_hwmon *pvt, long val)
+{
+       u32 trim;
+       int ret;
+
+       /*
+        * Serialize trim update, since a part of the register is changed and
+        * the controller is supposed to be disabled during this operation.
+        */
+       ret = mutex_lock_interruptible(&pvt->iface_mtx);
+       if (ret)
+               return ret;
+
+       trim = pvt_calc_trim(val);
+       pvt_set_trim(pvt, trim);
+
+       mutex_unlock(&pvt->iface_mtx);
+
+       return 0;
+}
+
+static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val)
+{
+       unsigned long rate;
+       ktime_t kt;
+       u32 data;
+
+       rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+       if (!rate)
+               return -ENODEV;
+
+       /*
+        * Don't bother with mutex here, since we just read data from MMIO.
+        * We also have to scale the ticks timeout up to compensate the
+        * ms-ns-data translations.
+        */
+       data = readl(pvt->regs + PVT_TTIMEOUT) + 1;
+
+       /*
+        * Calculate ref-clock based delay (Ttotal) between two consecutive
+        * data samples of the same sensor. So we first must calculate the
+        * delay introduced by the internal ref-clock timer (Tref * Fclk).
+        * Then add the constant timeout cuased by each conversion latency
+        * (Tmin). The basic formulae for each conversion is following:
+        *   Ttotal = Tref * Fclk + Tmin
+        * Note if alarms are enabled the sensors are polled one after
+        * another, so in order to have the delay being applicable for each
+        * sensor the requested value must be equally redistirbuted.
+        */
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+       kt = ktime_set(PVT_SENSORS_NUM * (u64)data, 0);
+       kt = ktime_divns(kt, rate);
+       kt = ktime_add_ns(kt, PVT_SENSORS_NUM * PVT_TOUT_MIN);
+#else
+       kt = ktime_set(data, 0);
+       kt = ktime_divns(kt, rate);
+       kt = ktime_add_ns(kt, PVT_TOUT_MIN);
+#endif
+
+       /* Return the result in msec as hwmon sysfs interface requires. */
+       *val = ktime_to_ms(kt);
+
+       return 0;
+}
+
+static int pvt_write_timeout(struct pvt_hwmon *pvt, long val)
+{
+       unsigned long rate;
+       ktime_t kt;
+       u32 data;
+       int ret;
+
+       rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
+       if (!rate)
+               return -ENODEV;
+
+       /*
+        * If alarms are enabled, the requested timeout must be divided
+        * between all available sensors to have the requested delay
+        * applicable to each individual sensor.
+        */
+       kt = ms_to_ktime(val);
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+       kt = ktime_divns(kt, PVT_SENSORS_NUM);
+#endif
+
+       /*
+        * Subtract a constant lag, which always persists due to the limited
+        * PVT sampling rate. Make sure the timeout is not negative.
+        */
+       kt = ktime_sub_ns(kt, PVT_TOUT_MIN);
+       if (ktime_to_ns(kt) < 0)
+               kt = ktime_set(0, 0);
+
+       /*
+        * Finally recalculate the timeout in terms of the reference clock
+        * period.
+        */
+       data = ktime_divns(kt * rate, NSEC_PER_SEC);
+
+       /*
+        * Update the measurements delay, but lock the interface first, since
+        * we have to disable PVT in order to have the new delay actually
+        * updated.
+        */
+       ret = mutex_lock_interruptible(&pvt->iface_mtx);
+       if (ret)
+               return ret;
+
+       pvt_set_tout(pvt, data);
+
+       mutex_unlock(&pvt->iface_mtx);
+
+       return 0;
+}
+
+static int pvt_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+                         u32 attr, int ch, long *val)
+{
+       struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+       if (!pvt_hwmon_channel_is_valid(type, ch))
+               return -EINVAL;
+
+       switch (type) {
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+                       return pvt_read_timeout(pvt, val);
+               }
+               break;
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_input:
+                       return pvt_read_data(pvt, ch, val);
+               case hwmon_temp_type:
+                       *val = 1;
+                       return 0;
+               case hwmon_temp_min:
+                       return pvt_read_limit(pvt, ch, true, val);
+               case hwmon_temp_max:
+                       return pvt_read_limit(pvt, ch, false, val);
+               case hwmon_temp_min_alarm:
+                       return pvt_read_alarm(pvt, ch, true, val);
+               case hwmon_temp_max_alarm:
+                       return pvt_read_alarm(pvt, ch, false, val);
+               case hwmon_temp_offset:
+                       return pvt_read_trim(pvt, val);
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+                       return pvt_read_data(pvt, PVT_VOLT + ch, val);
+               case hwmon_in_min:
+                       return pvt_read_limit(pvt, PVT_VOLT + ch, true, val);
+               case hwmon_in_max:
+                       return pvt_read_limit(pvt, PVT_VOLT + ch, false, val);
+               case hwmon_in_min_alarm:
+                       return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val);
+               case hwmon_in_max_alarm:
+                       return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int pvt_hwmon_read_string(struct device *dev,
+                                enum hwmon_sensor_types type,
+                                u32 attr, int ch, const char **str)
+{
+       if (!pvt_hwmon_channel_is_valid(type, ch))
+               return -EINVAL;
+
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_label:
+                       *str = pvt_info[ch].label;
+                       return 0;
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_label:
+                       *str = pvt_info[PVT_VOLT + ch].label;
+                       return 0;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int pvt_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+                          u32 attr, int ch, long val)
+{
+       struct pvt_hwmon *pvt = dev_get_drvdata(dev);
+
+       if (!pvt_hwmon_channel_is_valid(type, ch))
+               return -EINVAL;
+
+       switch (type) {
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_update_interval:
+                       return pvt_write_timeout(pvt, val);
+               }
+               break;
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_min:
+                       return pvt_write_limit(pvt, ch, true, val);
+               case hwmon_temp_max:
+                       return pvt_write_limit(pvt, ch, false, val);
+               case hwmon_temp_offset:
+                       return pvt_write_trim(pvt, val);
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_min:
+                       return pvt_write_limit(pvt, PVT_VOLT + ch, true, val);
+               case hwmon_in_max:
+                       return pvt_write_limit(pvt, PVT_VOLT + ch, false, val);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops pvt_hwmon_ops = {
+       .is_visible = pvt_hwmon_is_visible,
+       .read = pvt_hwmon_read,
+       .read_string = pvt_hwmon_read_string,
+       .write = pvt_hwmon_write
+};
+
+static const struct hwmon_chip_info pvt_hwmon_info = {
+       .ops = &pvt_hwmon_ops,
+       .info = pvt_channel_info
+};
+
+static void pvt_clear_data(void *data)
+{
+       struct pvt_hwmon *pvt = data;
+#if !defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+       int idx;
+
+       for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+               complete_all(&pvt->cache[idx].conversion);
+#endif
+
+       mutex_destroy(&pvt->iface_mtx);
+}
+
+static struct pvt_hwmon *pvt_create_data(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct pvt_hwmon *pvt;
+       int ret, idx;
+
+       pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL);
+       if (!pvt)
+               return ERR_PTR(-ENOMEM);
+
+       ret = devm_add_action(dev, pvt_clear_data, pvt);
+       if (ret) {
+               dev_err(dev, "Can't add PVT data clear action\n");
+               return ERR_PTR(ret);
+       }
+
+       pvt->dev = dev;
+       pvt->sensor = PVT_SENSOR_FIRST;
+       mutex_init(&pvt->iface_mtx);
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+       for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+               seqlock_init(&pvt->cache[idx].data_seqlock);
+#else
+       for (idx = 0; idx < PVT_SENSORS_NUM; ++idx)
+               init_completion(&pvt->cache[idx].conversion);
+#endif
+
+       return pvt;
+}
+
+static int pvt_request_regs(struct pvt_hwmon *pvt)
+{
+       struct platform_device *pdev = to_platform_device(pvt->dev);
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(pvt->dev, "Couldn't find PVT memresource\n");
+               return -EINVAL;
+       }
+
+       pvt->regs = devm_ioremap_resource(pvt->dev, res);
+       if (IS_ERR(pvt->regs)) {
+               dev_err(pvt->dev, "Couldn't map PVT registers\n");
+               return PTR_ERR(pvt->regs);
+       }
+
+       return 0;
+}
+
+static void pvt_disable_clks(void *data)
+{
+       struct pvt_hwmon *pvt = data;
+
+       clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks);
+}
+
+static int pvt_request_clks(struct pvt_hwmon *pvt)
+{
+       int ret;
+
+       pvt->clks[PVT_CLOCK_APB].id = "pclk";
+       pvt->clks[PVT_CLOCK_REF].id = "ref";
+
+       ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
+       if (ret) {
+               dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
+               return ret;
+       }
+
+       ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
+       if (ret) {
+               dev_err(pvt->dev, "Couldn't enable the PVT clocks\n");
+               return ret;
+       }
+
+       ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
+       if (ret) {
+               dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void pvt_init_iface(struct pvt_hwmon *pvt)
+{
+       u32 trim, temp;
+
+       /*
+        * Make sure all interrupts and controller are disabled so not to
+        * accidentally have ISR executed before the driver data is fully
+        * initialized. Clear the IRQ status as well.
+        */
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       readl(pvt->regs + PVT_CLR_INTR);
+       readl(pvt->regs + PVT_DATA);
+
+       /* Setup default sensor mode, timeout and temperature trim. */
+       pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
+       pvt_set_tout(pvt, PVT_TOUT_DEF);
+
+       trim = PVT_TRIM_DEF;
+       if (!of_property_read_u32(pvt->dev->of_node,
+            "baikal,pvt-temp-offset-millicelsius", &temp))
+               trim = pvt_calc_trim(temp);
+
+       pvt_set_trim(pvt, trim);
+}
+
+static int pvt_request_irq(struct pvt_hwmon *pvt)
+{
+       struct platform_device *pdev = to_platform_device(pvt->dev);
+       int ret;
+
+       pvt->irq = platform_get_irq(pdev, 0);
+       if (pvt->irq < 0)
+               return pvt->irq;
+
+       ret = devm_request_threaded_irq(pvt->dev, pvt->irq,
+                                       pvt_hard_isr, pvt_soft_isr,
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+                                       IRQF_SHARED | IRQF_TRIGGER_HIGH |
+                                       IRQF_ONESHOT,
+#else
+                                       IRQF_SHARED | IRQF_TRIGGER_HIGH,
+#endif
+                                       "pvt", pvt);
+       if (ret) {
+               dev_err(pvt->dev, "Couldn't request PVT IRQ\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pvt_create_hwmon(struct pvt_hwmon *pvt)
+{
+       pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt,
+               &pvt_hwmon_info, NULL);
+       if (IS_ERR(pvt->hwmon)) {
+               dev_err(pvt->dev, "Couldn't create hwmon device\n");
+               return PTR_ERR(pvt->hwmon);
+       }
+
+       return 0;
+}
+
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+
+static void pvt_disable_iface(void *data)
+{
+       struct pvt_hwmon *pvt = data;
+
+       mutex_lock(&pvt->iface_mtx);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+                  PVT_INTR_DVALID);
+       mutex_unlock(&pvt->iface_mtx);
+}
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+       int ret;
+
+       ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt);
+       if (ret) {
+               dev_err(pvt->dev, "Can't add PVT disable interface action\n");
+               return ret;
+       }
+
+       /*
+        * Enable sensors data conversion and IRQ. We need to lock the
+        * interface mutex since hwmon has just been created and the
+        * corresponding sysfs files are accessible from user-space,
+        * which theoretically may cause races.
+        */
+       mutex_lock(&pvt->iface_mtx);
+       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+       mutex_unlock(&pvt->iface_mtx);
+
+       return 0;
+}
+
+#else /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_enable_iface(struct pvt_hwmon *pvt)
+{
+       return 0;
+}
+
+#endif /* !CONFIG_SENSORS_BT1_PVT_ALARMS */
+
+static int pvt_probe(struct platform_device *pdev)
+{
+       struct pvt_hwmon *pvt;
+       int ret;
+
+       pvt = pvt_create_data(pdev);
+       if (IS_ERR(pvt))
+               return PTR_ERR(pvt);
+
+       ret = pvt_request_regs(pvt);
+       if (ret)
+               return ret;
+
+       ret = pvt_request_clks(pvt);
+       if (ret)
+               return ret;
+
+       pvt_init_iface(pvt);
+
+       ret = pvt_request_irq(pvt);
+       if (ret)
+               return ret;
+
+       ret = pvt_create_hwmon(pvt);
+       if (ret)
+               return ret;
+
+       ret = pvt_enable_iface(pvt);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static const struct of_device_id pvt_of_match[] = {
+       { .compatible = "baikal,bt1-pvt" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pvt_of_match);
+
+static struct platform_driver pvt_driver = {
+       .probe = pvt_probe,
+       .driver = {
+               .name = "bt1-pvt",
+               .of_match_table = pvt_of_match
+       }
+};
+module_platform_driver(pvt_driver);
+
+MODULE_AUTHOR("Maxim Kaurkin <maxim.kaurkin@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-T1 PVT driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
new file mode 100644 (file)
index 0000000..5eac73e
--- /dev/null
@@ -0,0 +1,244 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
+ *
+ * Baikal-T1 Process, Voltage, Temperature sensor driver
+ */
+#ifndef __HWMON_BT1_PVT_H__
+#define __HWMON_BT1_PVT_H__
+
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/seqlock.h>
+
+/* Baikal-T1 PVT registers and their bitfields */
+#define PVT_CTRL                       0x00
+#define PVT_CTRL_EN                    BIT(0)
+#define PVT_CTRL_MODE_FLD              1
+#define PVT_CTRL_MODE_MASK             GENMASK(3, PVT_CTRL_MODE_FLD)
+#define PVT_CTRL_MODE_TEMP             0x0
+#define PVT_CTRL_MODE_VOLT             0x1
+#define PVT_CTRL_MODE_LVT              0x2
+#define PVT_CTRL_MODE_HVT              0x4
+#define PVT_CTRL_MODE_SVT              0x6
+#define PVT_CTRL_TRIM_FLD              4
+#define PVT_CTRL_TRIM_MASK             GENMASK(8, PVT_CTRL_TRIM_FLD)
+#define PVT_DATA                       0x04
+#define PVT_DATA_VALID                 BIT(10)
+#define PVT_DATA_DATA_FLD              0
+#define PVT_DATA_DATA_MASK             GENMASK(9, PVT_DATA_DATA_FLD)
+#define PVT_TTHRES                     0x08
+#define PVT_VTHRES                     0x0C
+#define PVT_LTHRES                     0x10
+#define PVT_HTHRES                     0x14
+#define PVT_STHRES                     0x18
+#define PVT_THRES_LO_FLD               0
+#define PVT_THRES_LO_MASK              GENMASK(9, PVT_THRES_LO_FLD)
+#define PVT_THRES_HI_FLD               10
+#define PVT_THRES_HI_MASK              GENMASK(19, PVT_THRES_HI_FLD)
+#define PVT_TTIMEOUT                   0x1C
+#define PVT_INTR_STAT                  0x20
+#define PVT_INTR_MASK                  0x24
+#define PVT_RAW_INTR_STAT              0x28
+#define PVT_INTR_DVALID                        BIT(0)
+#define PVT_INTR_TTHRES_LO             BIT(1)
+#define PVT_INTR_TTHRES_HI             BIT(2)
+#define PVT_INTR_VTHRES_LO             BIT(3)
+#define PVT_INTR_VTHRES_HI             BIT(4)
+#define PVT_INTR_LTHRES_LO             BIT(5)
+#define PVT_INTR_LTHRES_HI             BIT(6)
+#define PVT_INTR_HTHRES_LO             BIT(7)
+#define PVT_INTR_HTHRES_HI             BIT(8)
+#define PVT_INTR_STHRES_LO             BIT(9)
+#define PVT_INTR_STHRES_HI             BIT(10)
+#define PVT_INTR_ALL                   GENMASK(10, 0)
+#define PVT_CLR_INTR                   0x2C
+
+/*
+ * PVT sensors-related limits and default values
+ * @PVT_TEMP_MIN: Minimal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_MAX: Maximal temperature in millidegrees of Celsius.
+ * @PVT_TEMP_CHS: Number of temperature hwmon channels.
+ * @PVT_VOLT_MIN: Minimal voltage in mV.
+ * @PVT_VOLT_MAX: Maximal voltage in mV.
+ * @PVT_VOLT_CHS: Number of voltage hwmon channels.
+ * @PVT_DATA_MIN: Minimal PVT raw data value.
+ * @PVT_DATA_MAX: Maximal PVT raw data value.
+ * @PVT_TRIM_MIN: Minimal temperature sensor trim value.
+ * @PVT_TRIM_MAX: Maximal temperature sensor trim value.
+ * @PVT_TRIM_DEF: Default temperature sensor trim value (set a proper value
+ *               when one is determined for Baikal-T1 SoC).
+ * @PVT_TRIM_TEMP: Maximum temperature encoded by the trim factor.
+ * @PVT_TRIM_STEP: Temperature stride corresponding to the trim value.
+ * @PVT_TOUT_MIN: Minimal timeout between samples in nanoseconds.
+ * @PVT_TOUT_DEF: Default data measurements timeout. In case if alarms are
+ *               activated the PVT IRQ is enabled to be raised after each
+ *               conversion in order to have the thresholds checked and the
+ *               converted value cached. Too frequent conversions may cause
+ *               the system CPU overload. Lets set the 50ms delay between
+ *               them by default to prevent this.
+ */
+#define PVT_TEMP_MIN           -48380L
+#define PVT_TEMP_MAX           147438L
+#define PVT_TEMP_CHS           1
+#define PVT_VOLT_MIN           620L
+#define PVT_VOLT_MAX           1168L
+#define PVT_VOLT_CHS           4
+#define PVT_DATA_MIN           0
+#define PVT_DATA_MAX           (PVT_DATA_DATA_MASK >> PVT_DATA_DATA_FLD)
+#define PVT_TRIM_MIN           0
+#define PVT_TRIM_MAX           (PVT_CTRL_TRIM_MASK >> PVT_CTRL_TRIM_FLD)
+#define PVT_TRIM_TEMP          7130
+#define PVT_TRIM_STEP          (PVT_TRIM_TEMP / PVT_TRIM_MAX)
+#define PVT_TRIM_DEF           0
+#define PVT_TOUT_MIN           (NSEC_PER_SEC / 3000)
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+# define PVT_TOUT_DEF          60000
+#else
+# define PVT_TOUT_DEF          0
+#endif
+
+/*
+ * enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT
+ *                       sampling mode)
+ * @PVT_SENSOR*: helpers to traverse the sensors in loops.
+ * @PVT_TEMP: PVT Temperature sensor.
+ * @PVT_VOLT: PVT Voltage sensor.
+ * @PVT_LVT: PVT Low-Voltage threshold sensor.
+ * @PVT_HVT: PVT High-Voltage threshold sensor.
+ * @PVT_SVT: PVT Standard-Voltage threshold sensor.
+ */
+enum pvt_sensor_type {
+       PVT_SENSOR_FIRST,
+       PVT_TEMP = PVT_SENSOR_FIRST,
+       PVT_VOLT,
+       PVT_LVT,
+       PVT_HVT,
+       PVT_SVT,
+       PVT_SENSOR_LAST = PVT_SVT,
+       PVT_SENSORS_NUM
+};
+
+/*
+ * enum pvt_clock_type - Baikal-T1 PVT clocks.
+ * @PVT_CLOCK_APB: APB clock.
+ * @PVT_CLOCK_REF: PVT reference clock.
+ */
+enum pvt_clock_type {
+       PVT_CLOCK_APB,
+       PVT_CLOCK_REF,
+       PVT_CLOCK_NUM
+};
+
+/*
+ * struct pvt_sensor_info - Baikal-T1 PVT sensor informational structure
+ * @channel: Sensor channel ID.
+ * @label: hwmon sensor label.
+ * @mode: PVT mode corresponding to the channel.
+ * @thres_base: upper and lower threshold values of the sensor.
+ * @thres_sts_lo: low threshold status bitfield.
+ * @thres_sts_hi: high threshold status bitfield.
+ * @type: Sensor type.
+ * @attr_min_alarm: Min alarm attribute ID.
+ * @attr_min_alarm: Max alarm attribute ID.
+ */
+struct pvt_sensor_info {
+       int channel;
+       const char *label;
+       u32 mode;
+       unsigned long thres_base;
+       u32 thres_sts_lo;
+       u32 thres_sts_hi;
+       enum hwmon_sensor_types type;
+       u32 attr_min_alarm;
+       u32 attr_max_alarm;
+};
+
+#define PVT_SENSOR_INFO(_ch, _label, _type, _mode, _thres)     \
+       {                                                       \
+               .channel = _ch,                                 \
+               .label = _label,                                \
+               .mode = PVT_CTRL_MODE_ ##_mode,                 \
+               .thres_base = PVT_ ##_thres,                    \
+               .thres_sts_lo = PVT_INTR_ ##_thres## _LO,       \
+               .thres_sts_hi = PVT_INTR_ ##_thres## _HI,       \
+               .type = _type,                                  \
+               .attr_min_alarm = _type## _min,                 \
+               .attr_max_alarm = _type## _max,                 \
+       }
+
+/*
+ * struct pvt_cache - PVT sensors data cache
+ * @data: data cache in raw format.
+ * @thres_sts_lo: low threshold status saved on the previous data conversion.
+ * @thres_sts_hi: high threshold status saved on the previous data conversion.
+ * @data_seqlock: cached data seq-lock.
+ * @conversion: data conversion completion.
+ */
+struct pvt_cache {
+       u32 data;
+#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
+       seqlock_t data_seqlock;
+       u32 thres_sts_lo;
+       u32 thres_sts_hi;
+#else
+       struct completion conversion;
+#endif
+};
+
+/*
+ * struct pvt_hwmon - Baikal-T1 PVT private data
+ * @dev: device structure of the PVT platform device.
+ * @hwmon: hwmon device structure.
+ * @regs: pointer to the Baikal-T1 PVT registers region.
+ * @irq: PVT events IRQ number.
+ * @clks: Array of the PVT clocks descriptor (APB/ref clocks).
+ * @ref_clk: Pointer to the reference clocks descriptor.
+ * @iface_mtx: Generic interface mutex (used to lock the alarm registers
+ *            when the alarms enabled, or the data conversion interface
+ *            if alarms are disabled).
+ * @sensor: current PVT sensor the data conversion is being performed for.
+ * @cache: data cache descriptor.
+ */
+struct pvt_hwmon {
+       struct device *dev;
+       struct device *hwmon;
+
+       void __iomem *regs;
+       int irq;
+
+       struct clk_bulk_data clks[PVT_CLOCK_NUM];
+
+       struct mutex iface_mtx;
+       enum pvt_sensor_type sensor;
+       struct pvt_cache cache[PVT_SENSORS_NUM];
+};
+
+/*
+ * struct pvt_poly_term - a term descriptor of the PVT data translation
+ *                       polynomial
+ * @deg: degree of the term.
+ * @coef: multiplication factor of the term.
+ * @divider: distributed divider per each degree.
+ * @divider_leftover: divider leftover, which couldn't be redistributed.
+ */
+struct pvt_poly_term {
+       unsigned int deg;
+       long coef;
+       long divider;
+       long divider_leftover;
+};
+
+/*
+ * struct pvt_poly - PVT data translation polynomial descriptor
+ * @total_divider: total data divider.
+ * @terms: polynomial terms up to a free one.
+ */
+struct pvt_poly {
+       long total_divider;
+       struct pvt_poly_term terms[];
+};
+
+#endif /* __HWMON_BT1_PVT_H__ */
index ab719d372b0dee8e4fddc7c8218b5704f81d1528..16be012a95ed845e4f4376cfac6f3cb4c3451079 100644 (file)
@@ -1072,13 +1072,6 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
                },
        },
-       {
-               .ident = "Dell XPS421",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
-               },
-       },
        {
                .ident = "Dell Studio",
                .matches = {
@@ -1087,14 +1080,6 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
                },
                .driver_data = (void *)&i8k_config_data[DELL_STUDIO],
        },
-       {
-               .ident = "Dell XPS 13",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS13"),
-               },
-               .driver_data = (void *)&i8k_config_data[DELL_XPS],
-       },
        {
                .ident = "Dell XPS M140",
                .matches = {
@@ -1104,17 +1089,10 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
                .driver_data = (void *)&i8k_config_data[DELL_XPS],
        },
        {
-               .ident = "Dell XPS 15 9560",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9560"),
-               },
-       },
-       {
-               .ident = "Dell XPS 15 9570",
+               .ident = "Dell XPS",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS 15 9570"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "XPS"),
                },
        },
        { }
diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c
new file mode 100644 (file)
index 0000000..2137bc6
--- /dev/null
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Gateworks System Controller Hardware Monitor module
+ *
+ * Copyright (C) 2020 Gateworks Corporation
+ */
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/gsc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/gsc_hwmon.h>
+
+#define GSC_HWMON_MAX_TEMP_CH  16
+#define GSC_HWMON_MAX_IN_CH    16
+
+#define GSC_HWMON_RESOLUTION   12
+#define GSC_HWMON_VREF         2500
+
+struct gsc_hwmon_data {
+       struct gsc_dev *gsc;
+       struct gsc_hwmon_platform_data *pdata;
+       struct regmap *regmap;
+       const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH];
+       const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH];
+       u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1];
+       u32 in_config[GSC_HWMON_MAX_IN_CH + 1];
+       struct hwmon_channel_info temp_info;
+       struct hwmon_channel_info in_info;
+       const struct hwmon_channel_info *info[3];
+       struct hwmon_chip_info chip;
+};
+
+static struct regmap_bus gsc_hwmon_regmap_bus = {
+       .reg_read = gsc_read,
+       .reg_write = gsc_write,
+};
+
+static const struct regmap_config gsc_hwmon_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .cache_type = REGCACHE_NONE,
+};
+
+static ssize_t pwm_auto_point_temp_show(struct device *dev,
+                                       struct device_attribute *devattr,
+                                       char *buf)
+{
+       struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       u8 reg = hwmon->pdata->fan_base + (2 * attr->index);
+       u8 regs[2];
+       int ret;
+
+       ret = regmap_bulk_read(hwmon->regmap, reg, regs, 2);
+       if (ret)
+               return ret;
+
+       ret = regs[0] | regs[1] << 8;
+       return sprintf(buf, "%d\n", ret * 10);
+}
+
+static ssize_t pwm_auto_point_temp_store(struct device *dev,
+                                        struct device_attribute *devattr,
+                                        const char *buf, size_t count)
+{
+       struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       u8 reg = hwmon->pdata->fan_base + (2 * attr->index);
+       u8 regs[2];
+       long temp;
+       int err;
+
+       if (kstrtol(buf, 10, &temp))
+               return -EINVAL;
+
+       temp = clamp_val(temp, 0, 10000);
+       temp = DIV_ROUND_CLOSEST(temp, 10);
+
+       regs[0] = temp & 0xff;
+       regs[1] = (temp >> 8) & 0xff;
+       err = regmap_bulk_write(hwmon->regmap, reg, regs, 2);
+       if (err)
+               return err;
+
+       return count;
+}
+
+static ssize_t pwm_auto_point_pwm_show(struct device *dev,
+                                      struct device_attribute *devattr,
+                                      char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10)) / 100);
+}
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_auto_point_temp, 0);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_pwm, pwm_auto_point_pwm, 1);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, pwm_auto_point_temp, 1);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm_auto_point_pwm, 2);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_auto_point_temp, 2);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point4_pwm, pwm_auto_point_pwm, 3);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_auto_point_temp, 3);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point5_pwm, pwm_auto_point_pwm, 4);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_auto_point_temp, 4);
+
+static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point6_pwm, pwm_auto_point_pwm, 5);
+static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point6_temp, pwm_auto_point_temp, 5);
+
+static struct attribute *gsc_hwmon_attributes[] = {
+       &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group gsc_hwmon_group = {
+       .attrs = gsc_hwmon_attributes,
+};
+__ATTRIBUTE_GROUPS(gsc_hwmon);
+
+static int
+gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+              int channel, long *val)
+{
+       struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+       const struct gsc_hwmon_channel *ch;
+       int sz, ret;
+       long tmp;
+       u8 buf[3];
+
+       switch (type) {
+       case hwmon_in:
+               ch = hwmon->in_ch[channel];
+               break;
+       case hwmon_temp:
+               ch = hwmon->temp_ch[channel];
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       sz = (ch->mode == mode_voltage) ? 3 : 2;
+       ret = regmap_bulk_read(hwmon->regmap, ch->reg, buf, sz);
+       if (ret)
+               return ret;
+
+       tmp = 0;
+       while (sz-- > 0)
+               tmp |= (buf[sz] << (8 * sz));
+
+       switch (ch->mode) {
+       case mode_temperature:
+               if (tmp > 0x8000)
+                       tmp -= 0xffff;
+               break;
+       case mode_voltage_raw:
+               tmp = clamp_val(tmp, 0, BIT(GSC_HWMON_RESOLUTION));
+               /* scale based on ref voltage and ADC resolution */
+               tmp *= GSC_HWMON_VREF;
+               tmp >>= GSC_HWMON_RESOLUTION;
+               /* scale based on optional voltage divider */
+               if (ch->vdiv[0] && ch->vdiv[1]) {
+                       tmp *= (ch->vdiv[0] + ch->vdiv[1]);
+                       tmp /= ch->vdiv[1];
+               }
+               /* adjust by uV offset */
+               tmp += ch->mvoffset;
+               break;
+       case mode_voltage:
+               /* no adjustment needed */
+               break;
+       }
+
+       *val = tmp;
+
+       return 0;
+}
+
+static int
+gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+                     u32 attr, int channel, const char **buf)
+{
+       struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+
+       switch (type) {
+       case hwmon_in:
+               *buf = hwmon->in_ch[channel]->name;
+               break;
+       case hwmon_temp:
+               *buf = hwmon->temp_ch[channel]->name;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
+}
+
+static umode_t
+gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
+                    int ch)
+{
+       return 0444;
+}
+
+static const struct hwmon_ops gsc_hwmon_ops = {
+       .is_visible = gsc_hwmon_is_visible,
+       .read = gsc_hwmon_read,
+       .read_string = gsc_hwmon_read_string,
+};
+
+static struct gsc_hwmon_platform_data *
+gsc_hwmon_get_devtree_pdata(struct device *dev)
+{
+       struct gsc_hwmon_platform_data *pdata;
+       struct gsc_hwmon_channel *ch;
+       struct fwnode_handle *child;
+       struct device_node *fan;
+       int nchannels;
+
+       nchannels = device_get_child_node_count(dev);
+       if (nchannels == 0)
+               return ERR_PTR(-ENODEV);
+
+       pdata = devm_kzalloc(dev,
+                            sizeof(*pdata) + nchannels * sizeof(*ch),
+                            GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+       ch = (struct gsc_hwmon_channel *)(pdata + 1);
+       pdata->channels = ch;
+       pdata->nchannels = nchannels;
+
+       /* fan controller base address */
+       fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
+       if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
+               dev_err(dev, "fan node without base\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       /* allocate structures for channels and count instances of each type */
+       device_for_each_child_node(dev, child) {
+               if (fwnode_property_read_string(child, "label", &ch->name)) {
+                       dev_err(dev, "channel without label\n");
+                       fwnode_handle_put(child);
+                       return ERR_PTR(-EINVAL);
+               }
+               if (fwnode_property_read_u32(child, "reg", &ch->reg)) {
+                       dev_err(dev, "channel without reg\n");
+                       fwnode_handle_put(child);
+                       return ERR_PTR(-EINVAL);
+               }
+               if (fwnode_property_read_u32(child, "gw,mode", &ch->mode)) {
+                       dev_err(dev, "channel without mode\n");
+                       fwnode_handle_put(child);
+                       return ERR_PTR(-EINVAL);
+               }
+               if (ch->mode > mode_max) {
+                       dev_err(dev, "invalid channel mode\n");
+                       fwnode_handle_put(child);
+                       return ERR_PTR(-EINVAL);
+               }
+
+               if (!fwnode_property_read_u32(child,
+                                             "gw,voltage-offset-microvolt",
+                                             &ch->mvoffset))
+                       ch->mvoffset /= 1000;
+               fwnode_property_read_u32_array(child,
+                                              "gw,voltage-divider-ohms",
+                                              ch->vdiv, ARRAY_SIZE(ch->vdiv));
+               ch++;
+       }
+
+       return pdata;
+}
+
+static int gsc_hwmon_probe(struct platform_device *pdev)
+{
+       struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent);
+       struct device *dev = &pdev->dev;
+       struct device *hwmon_dev;
+       struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev);
+       struct gsc_hwmon_data *hwmon;
+       const struct attribute_group **groups;
+       int i, i_in, i_temp;
+
+       if (!pdata) {
+               pdata = gsc_hwmon_get_devtree_pdata(dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       }
+
+       hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
+       if (!hwmon)
+               return -ENOMEM;
+       hwmon->gsc = gsc;
+       hwmon->pdata = pdata;
+
+       hwmon->regmap = devm_regmap_init(dev, &gsc_hwmon_regmap_bus,
+                                        gsc->i2c_hwmon,
+                                        &gsc_hwmon_regmap_config);
+       if (IS_ERR(hwmon->regmap))
+               return PTR_ERR(hwmon->regmap);
+
+       for (i = 0, i_in = 0, i_temp = 0; i < hwmon->pdata->nchannels; i++) {
+               const struct gsc_hwmon_channel *ch = &pdata->channels[i];
+
+               switch (ch->mode) {
+               case mode_temperature:
+                       if (i_temp == GSC_HWMON_MAX_TEMP_CH) {
+                               dev_err(gsc->dev, "too many temp channels\n");
+                               return -EINVAL;
+                       }
+                       hwmon->temp_ch[i_temp] = ch;
+                       hwmon->temp_config[i_temp] = HWMON_T_INPUT |
+                                                    HWMON_T_LABEL;
+                       i_temp++;
+                       break;
+               case mode_voltage:
+               case mode_voltage_raw:
+                       if (i_in == GSC_HWMON_MAX_IN_CH) {
+                               dev_err(gsc->dev, "too many input channels\n");
+                               return -EINVAL;
+                       }
+                       hwmon->in_ch[i_in] = ch;
+                       hwmon->in_config[i_in] =
+                               HWMON_I_INPUT | HWMON_I_LABEL;
+                       i_in++;
+                       break;
+               default:
+                       dev_err(gsc->dev, "invalid mode: %d\n", ch->mode);
+                       return -EINVAL;
+               }
+       }
+
+       /* setup config structures */
+       hwmon->chip.ops = &gsc_hwmon_ops;
+       hwmon->chip.info = hwmon->info;
+       hwmon->info[0] = &hwmon->temp_info;
+       hwmon->info[1] = &hwmon->in_info;
+       hwmon->temp_info.type = hwmon_temp;
+       hwmon->temp_info.config = hwmon->temp_config;
+       hwmon->in_info.type = hwmon_in;
+       hwmon->in_info.config = hwmon->in_config;
+
+       groups = pdata->fan_base ? gsc_hwmon_groups : NULL;
+       hwmon_dev = devm_hwmon_device_register_with_info(dev,
+                                                        KBUILD_MODNAME, hwmon,
+                                                        &hwmon->chip, groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id gsc_hwmon_of_match[] = {
+       { .compatible = "gw,gsc-adc", },
+       {}
+};
+
+static struct platform_driver gsc_hwmon_driver = {
+       .driver = {
+               .name = "gsc-hwmon",
+               .of_match_table = gsc_hwmon_of_match,
+       },
+       .probe = gsc_hwmon_probe,
+};
+
+module_platform_driver(gsc_hwmon_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("GSC hardware monitor driver");
+MODULE_LICENSE("GPL v2");
index 6a30fb453f7adce591062868866e8dda6f158f8d..3f596a5328da3f80c46afc40e56397bf96c3480d 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/gfp.h>
 #include <linux/hwmon.h>
 #include <linux/idr.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -31,7 +32,7 @@ struct hwmon_device {
        const char *name;
        struct device dev;
        const struct hwmon_chip_info *chip;
-
+       struct list_head tzdata;
        struct attribute_group group;
        const struct attribute_group **groups;
 };
@@ -55,12 +56,12 @@ struct hwmon_device_attribute {
 
 /*
  * Thermal zone information
- * In addition to the reference to the hwmon device,
- * also provides the sensor index.
  */
 struct hwmon_thermal_data {
+       struct list_head node;          /* hwmon tzdata list entry */
        struct device *dev;             /* Reference to hwmon device */
        int index;                      /* sensor index */
+       struct thermal_zone_device *tzd;/* thermal zone device */
 };
 
 static ssize_t
@@ -156,10 +157,17 @@ static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
        .get_temp = hwmon_thermal_get_temp,
 };
 
+static void hwmon_thermal_remove_sensor(void *data)
+{
+       list_del(data);
+}
+
 static int hwmon_thermal_add_sensor(struct device *dev, int index)
 {
+       struct hwmon_device *hwdev = to_hwmon_device(dev);
        struct hwmon_thermal_data *tdata;
        struct thermal_zone_device *tzd;
+       int err;
 
        tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
        if (!tdata)
@@ -177,13 +185,68 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index)
        if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
                return PTR_ERR(tzd);
 
+       err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node);
+       if (err)
+               return err;
+
+       tdata->tzd = tzd;
+       list_add(&tdata->node, &hwdev->tzdata);
+
        return 0;
 }
+
+static int hwmon_thermal_register_sensors(struct device *dev)
+{
+       struct hwmon_device *hwdev = to_hwmon_device(dev);
+       const struct hwmon_chip_info *chip = hwdev->chip;
+       const struct hwmon_channel_info **info = chip->info;
+       void *drvdata = dev_get_drvdata(dev);
+       int i;
+
+       for (i = 1; info[i]; i++) {
+               int j;
+
+               if (info[i]->type != hwmon_temp)
+                       continue;
+
+               for (j = 0; info[i]->config[j]; j++) {
+                       int err;
+
+                       if (!(info[i]->config[j] & HWMON_T_INPUT) ||
+                           !chip->ops->is_visible(drvdata, hwmon_temp,
+                                                  hwmon_temp_input, j))
+                               continue;
+
+                       err = hwmon_thermal_add_sensor(dev, j);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static void hwmon_thermal_notify(struct device *dev, int index)
+{
+       struct hwmon_device *hwdev = to_hwmon_device(dev);
+       struct hwmon_thermal_data *tzdata;
+
+       list_for_each_entry(tzdata, &hwdev->tzdata, node) {
+               if (tzdata->index == index) {
+                       thermal_zone_device_update(tzdata->tzd,
+                                                  THERMAL_EVENT_UNSPECIFIED);
+               }
+       }
+}
+
 #else
-static int hwmon_thermal_add_sensor(struct device *dev, int index)
+static int hwmon_thermal_register_sensors(struct device *dev)
 {
        return 0;
 }
+
+static void hwmon_thermal_notify(struct device *dev, int index) { }
+
 #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
 
 static int hwmon_attr_base(enum hwmon_sensor_types type)
@@ -511,6 +574,35 @@ static const int __templates_size[] = {
        [hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates),
 };
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+                      u32 attr, int channel)
+{
+       char sattr[MAX_SYSFS_ATTR_NAME_LENGTH];
+       const char * const *templates;
+       const char *template;
+       int base;
+
+       if (type >= ARRAY_SIZE(__templates))
+               return -EINVAL;
+       if (attr >= __templates_size[type])
+               return -EINVAL;
+
+       templates = __templates[type];
+       template = templates[attr];
+
+       base = hwmon_attr_base(type);
+
+       scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel);
+       sysfs_notify(&dev->kobj, NULL, sattr);
+       kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+
+       if (type == hwmon_temp)
+               hwmon_thermal_notify(dev, channel);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hwmon_notify_event);
+
 static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
 {
        int i, n;
@@ -596,7 +688,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
 {
        struct hwmon_device *hwdev;
        struct device *hdev;
-       int i, j, err, id;
+       int i, err, id;
 
        /* Complain about invalid characters in hwmon name attribute */
        if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
@@ -661,33 +753,19 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
        if (err)
                goto free_hwmon;
 
+       INIT_LIST_HEAD(&hwdev->tzdata);
+
        if (dev && dev->of_node && chip && chip->ops->read &&
            chip->info[0]->type == hwmon_chip &&
            (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
-               const struct hwmon_channel_info **info = chip->info;
-
-               for (i = 1; info[i]; i++) {
-                       if (info[i]->type != hwmon_temp)
-                               continue;
-
-                       for (j = 0; info[i]->config[j]; j++) {
-                               if (!chip->ops->is_visible(drvdata, hwmon_temp,
-                                                          hwmon_temp_input, j))
-                                       continue;
-                               if (info[i]->config[j] & HWMON_T_INPUT) {
-                                       err = hwmon_thermal_add_sensor(hdev, j);
-                                       if (err) {
-                                               device_unregister(hdev);
-                                               /*
-                                                * Don't worry about hwdev;
-                                                * hwmon_dev_release(), called
-                                                * from device_unregister(),
-                                                * will free it.
-                                                */
-                                               goto ida_remove;
-                                       }
-                               }
-                       }
+               err = hwmon_thermal_register_sensors(hdev);
+               if (err) {
+                       device_unregister(hdev);
+                       /*
+                        * Don't worry about hwdev; hwmon_dev_release(), called
+                        * from device_unregister(), will free it.
+                        */
+                       goto ida_remove;
                }
        }
 
index e9e78c0b7212441c188dd6aae2c22e89bd15ee2e..55d474ec7c35fea15e4ffa00ad92b6871687636e 100644 (file)
 #define INA226_READ_AVG(reg)           (((reg) & INA226_AVG_RD_MASK) >> 9)
 #define INA226_SHIFT_AVG(val)          ((val) << 9)
 
+/* bit number of alert functions in Mask/Enable Register */
+#define INA226_SHUNT_OVER_VOLTAGE_BIT  15
+#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14
+#define INA226_BUS_OVER_VOLTAGE_BIT    13
+#define INA226_BUS_UNDER_VOLTAGE_BIT   12
+#define INA226_POWER_OVER_LIMIT_BIT    11
+
+/* bit mask for alert config bits of Mask/Enable Register */
+#define INA226_ALERT_CONFIG_MASK       0xFC00
+#define INA226_ALERT_FUNCTION_FLAG     BIT(4)
+
 /* common attrs, ina226 attrs and NULL */
 #define INA2XX_MAX_ATTRIBUTE_GROUPS    3
 
@@ -303,6 +314,145 @@ static ssize_t ina2xx_value_show(struct device *dev,
                        ina2xx_get_value(data, attr->index, regval));
 }
 
+static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval)
+{
+       int reg;
+
+       switch (bit) {
+       case INA226_SHUNT_OVER_VOLTAGE_BIT:
+       case INA226_SHUNT_UNDER_VOLTAGE_BIT:
+               reg = INA2XX_SHUNT_VOLTAGE;
+               break;
+       case INA226_BUS_OVER_VOLTAGE_BIT:
+       case INA226_BUS_UNDER_VOLTAGE_BIT:
+               reg = INA2XX_BUS_VOLTAGE;
+               break;
+       case INA226_POWER_OVER_LIMIT_BIT:
+               reg = INA2XX_POWER;
+               break;
+       default:
+               /* programmer goofed */
+               WARN_ON_ONCE(1);
+               return 0;
+       }
+
+       return ina2xx_get_value(data, reg, regval);
+}
+
+/*
+ * Turns alert limit values into register values.
+ * Opposite of the formula in ina2xx_get_value().
+ */
+static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val)
+{
+       switch (bit) {
+       case INA226_SHUNT_OVER_VOLTAGE_BIT:
+       case INA226_SHUNT_UNDER_VOLTAGE_BIT:
+               val *= data->config->shunt_div;
+               return clamp_val(val, SHRT_MIN, SHRT_MAX);
+       case INA226_BUS_OVER_VOLTAGE_BIT:
+       case INA226_BUS_UNDER_VOLTAGE_BIT:
+               val = (val * 1000) << data->config->bus_voltage_shift;
+               val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
+               return clamp_val(val, 0, SHRT_MAX);
+       case INA226_POWER_OVER_LIMIT_BIT:
+               val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
+               return clamp_val(val, 0, USHRT_MAX);
+       default:
+               /* programmer goofed */
+               WARN_ON_ONCE(1);
+               return 0;
+       }
+}
+
+static ssize_t ina226_alert_show(struct device *dev,
+                                struct device_attribute *da, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       int regval;
+       int val = 0;
+       int ret;
+
+       mutex_lock(&data->config_lock);
+       ret = regmap_read(data->regmap, INA226_MASK_ENABLE, &regval);
+       if (ret)
+               goto abort;
+
+       if (regval & BIT(attr->index)) {
+               ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, &regval);
+               if (ret)
+                       goto abort;
+               val = ina226_reg_to_alert(data, attr->index, regval);
+       }
+
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
+abort:
+       mutex_unlock(&data->config_lock);
+       return ret;
+}
+
+static ssize_t ina226_alert_store(struct device *dev,
+                                 struct device_attribute *da,
+                                 const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Clear all alerts first to avoid accidentally triggering ALERT pin
+        * due to register write sequence. Then, only enable the alert
+        * if the value is non-zero.
+        */
+       mutex_lock(&data->config_lock);
+       ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
+                                INA226_ALERT_CONFIG_MASK, 0);
+       if (ret < 0)
+               goto abort;
+
+       ret = regmap_write(data->regmap, INA226_ALERT_LIMIT,
+                          ina226_alert_to_reg(data, attr->index, val));
+       if (ret < 0)
+               goto abort;
+
+       if (val != 0) {
+               ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
+                                        INA226_ALERT_CONFIG_MASK,
+                                        BIT(attr->index));
+               if (ret < 0)
+                       goto abort;
+       }
+
+       ret = count;
+abort:
+       mutex_unlock(&data->config_lock);
+       return ret;
+}
+
+static ssize_t ina226_alarm_show(struct device *dev,
+                                struct device_attribute *da, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       int regval;
+       int alarm = 0;
+       int ret;
+
+       ret = regmap_read(data->regmap, INA226_MASK_ENABLE, &regval);
+       if (ret)
+               return ret;
+
+       alarm = (regval & BIT(attr->index)) &&
+               (regval & INA226_ALERT_FUNCTION_FLAG);
+       return snprintf(buf, PAGE_SIZE, "%d\n", alarm);
+}
+
 /*
  * In order to keep calibration register value fixed, the product
  * of current_lsb and shunt_resistor should also be fixed and equal
@@ -392,15 +542,38 @@ static ssize_t ina226_interval_show(struct device *dev,
 
 /* shunt voltage */
 static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE);
+/* shunt voltage over/under voltage alert setting and alarm */
+static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert,
+                            INA226_SHUNT_OVER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert,
+                            INA226_SHUNT_UNDER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm,
+                            INA226_SHUNT_OVER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm,
+                            INA226_SHUNT_UNDER_VOLTAGE_BIT);
 
 /* bus voltage */
 static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE);
+/* bus voltage over/under voltage alert setting and alarm */
+static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert,
+                            INA226_BUS_OVER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert,
+                            INA226_BUS_UNDER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm,
+                            INA226_BUS_OVER_VOLTAGE_BIT);
+static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm,
+                            INA226_BUS_UNDER_VOLTAGE_BIT);
 
 /* calculated current */
 static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT);
 
 /* calculated power */
 static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER);
+/* over-limit power alert setting and alarm */
+static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert,
+                            INA226_POWER_OVER_LIMIT_BIT);
+static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm,
+                            INA226_POWER_OVER_LIMIT_BIT);
 
 /* shunt resistance */
 static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION);
@@ -423,6 +596,16 @@ static const struct attribute_group ina2xx_group = {
 };
 
 static struct attribute *ina226_attrs[] = {
+       &sensor_dev_attr_in0_crit.dev_attr.attr,
+       &sensor_dev_attr_in0_lcrit.dev_attr.attr,
+       &sensor_dev_attr_in0_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr,
+       &sensor_dev_attr_in1_crit.dev_attr.attr,
+       &sensor_dev_attr_in1_lcrit.dev_attr.attr,
+       &sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr,
+       &sensor_dev_attr_power1_crit.dev_attr.attr,
+       &sensor_dev_attr_power1_crit_alarm.dev_attr.attr,
        &sensor_dev_attr_update_interval.dev_attr.attr,
        NULL,
 };
index 4122e59f0bb4cfddc877a4ab3bb44ef13904a77f..ae2b84263a4455a4ed5258e6d272adc945565f1f 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
 #include <linux/of_device.h>
-
+#include <linux/acpi.h>
 
 #define DRVNAME                "lm70"
 
@@ -148,18 +148,50 @@ static const struct of_device_id lm70_of_ids[] = {
 MODULE_DEVICE_TABLE(of, lm70_of_ids);
 #endif
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id lm70_acpi_ids[] = {
+       {
+               .id = "LM000070",
+               .driver_data = LM70_CHIP_LM70,
+       },
+       {
+               .id = "TMP00121",
+               .driver_data = LM70_CHIP_TMP121,
+       },
+       {
+               .id = "LM000071",
+               .driver_data = LM70_CHIP_LM71,
+       },
+       {
+               .id = "LM000074",
+               .driver_data = LM70_CHIP_LM74,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, lm70_acpi_ids);
+#endif
+
 static int lm70_probe(struct spi_device *spi)
 {
-       const struct of_device_id *match;
+       const struct of_device_id *of_match;
        struct device *hwmon_dev;
        struct lm70 *p_lm70;
        int chip;
 
-       match = of_match_device(lm70_of_ids, &spi->dev);
-       if (match)
-               chip = (int)(uintptr_t)match->data;
-       else
-               chip = spi_get_device_id(spi)->driver_data;
+       of_match = of_match_device(lm70_of_ids, &spi->dev);
+       if (of_match)
+               chip = (int)(uintptr_t)of_match->data;
+       else {
+#ifdef CONFIG_ACPI
+               const struct acpi_device_id *acpi_match;
+
+               acpi_match = acpi_match_device(lm70_acpi_ids, &spi->dev);
+               if (acpi_match)
+                       chip = (int)(uintptr_t)acpi_match->driver_data;
+               else
+#endif
+                       chip = spi_get_device_id(spi)->driver_data;
+       }
 
        /* signaling is SPI_MODE_0 */
        if (spi->mode & (SPI_CPOL | SPI_CPHA))
@@ -195,6 +227,7 @@ static struct spi_driver lm70_driver = {
        .driver = {
                .name   = "lm70",
                .of_match_table = of_match_ptr(lm70_of_ids),
+               .acpi_match_table = ACPI_PTR(lm70_acpi_ids),
        },
        .id_table = lm70_ids,
        .probe  = lm70_probe,
index 5e6392294c0351e529754bef61426dbdcd35876d..ba0be48aeadddb6e1741953a5bfc8a83228c7d5c 100644 (file)
@@ -797,8 +797,10 @@ static int lm75_detect(struct i2c_client *new_client,
 
        /* First check for LM75A */
        if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) {
-               /* LM75A returns 0xff on unused registers so
-                  just to be sure we check for that too. */
+               /*
+                * LM75A returns 0xff on unused registers so
+                * just to be sure we check for that too.
+                */
                if (i2c_smbus_read_byte_data(new_client, 4) != 0xff
                 || i2c_smbus_read_byte_data(new_client, 5) != 0xff
                 || i2c_smbus_read_byte_data(new_client, 6) != 0xff)
@@ -849,6 +851,7 @@ static int lm75_suspend(struct device *dev)
 {
        int status;
        struct i2c_client *client = to_i2c_client(dev);
+
        status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
        if (status < 0) {
                dev_dbg(&client->dev, "Can't read config? %d\n", status);
@@ -863,6 +866,7 @@ static int lm75_resume(struct device *dev)
 {
        int status;
        struct i2c_client *client = to_i2c_client(dev);
+
        status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
        if (status < 0) {
                dev_dbg(&client->dev, "Can't read config? %d\n", status);
index b614e63285662d5e93a8963f7e56867853091c7e..a398171162a833be84342e699d0fc07ad4eba391 100644 (file)
@@ -1,17 +1,15 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
-    lm75.h - Part of lm_sensors, Linux kernel modules for hardware
-             monitoring
-    Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
-
-*/
+ * lm75.h - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ * Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
+ */
 
 /*
   This file contains common code for encoding/decoding LM75 type
   temperature readings, which are emulated by many of the chips
   we support.  As the user is unlikely to load more than one driver
   which contains this code, we don't worry about the wasted space.
-*/
* This file contains common code for encoding/decoding LM75 type
* temperature readings, which are emulated by many of the chips
* we support.  As the user is unlikely to load more than one driver
* which contains this code, we don't worry about the wasted space.
+ */
 
 #include <linux/kernel.h>
 
 #define LM75_TEMP_MAX 125000
 #define LM75_SHUTDOWN 0x01
 
-/* TEMP: 0.001C/bit (-55C to +125C)
-   REG: (0.5C/bit, two's complement) << 7 */
+/*
+ * TEMP: 0.001C/bit (-55C to +125C)
+ * REG: (0.5C/bit, two's complement) << 7
+ */
 static inline u16 LM75_TEMP_TO_REG(long temp)
 {
        int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
+
        ntemp += (ntemp < 0 ? -250 : 250);
        return (u16)((ntemp / 500) << 7);
 }
 
 static inline int LM75_TEMP_FROM_REG(u16 reg)
 {
-       /* use integer division instead of equivalent right shift to
-          guarantee arithmetic shift and preserve the sign */
+       /*
+        * use integer division instead of equivalent right shift to
+        * guarantee arithmetic shift and preserve the sign
+        */
        return ((s16)reg / 128) * 500;
 }
index 9b3c9f390ef816b699cb8da32487021606e24161..7bdc664af55b52ed4b78d14e1ab81934dabe8c9c 100644 (file)
  * explicitly as max6659, or if its address is not 0x4c.
  * These chips lack the remote temperature offset feature.
  *
+ * This driver also supports the MAX6654 chip made by Maxim. This chip can
+ * be at 9 different addresses, similar to MAX6680/MAX6681. The MAX6654 is
+ * otherwise similar to MAX6657/MAX6658/MAX6659. Extended range is available
+ * by setting the configuration register accordingly, and is done during
+ * initialization. Extended precision is only available at conversion rates
+ * of 1 Hz and slower. Note that extended precision is not enabled by
+ * default, as this driver initializes all chips to 2 Hz by design.
+ *
  * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and
  * MAX6692 chips made by Maxim.  These are again similar to the LM86,
  * but they use unsigned temperature values and can report temperatures
  * have address 0x4d.
  * MAX6647 has address 0x4e.
  * MAX6659 can have address 0x4c, 0x4d or 0x4e.
- * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
- * 0x4c, 0x4d or 0x4e.
+ * MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29,
+ * 0x2a, 0x2b, 0x4c, 0x4d or 0x4e.
  * SA56004 can have address 0x48 through 0x4F.
  */
 
@@ -104,7 +112,7 @@ static const unsigned short normal_i2c[] = {
        0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
-       max6646, w83l771, max6696, sa56004, g781, tmp451 };
+       max6646, w83l771, max6696, sa56004, g781, tmp451, max6654 };
 
 /*
  * The LM90 registers
@@ -145,7 +153,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define LM90_REG_R_TCRIT_HYST          0x21
 #define LM90_REG_W_TCRIT_HYST          0x21
 
-/* MAX6646/6647/6649/6657/6658/6659/6695/6696 registers */
+/* MAX6646/6647/6649/6654/6657/6658/6659/6695/6696 registers */
 
 #define MAX6657_REG_R_LOCAL_TEMPL      0x11
 #define MAX6696_REG_R_STATUS2          0x12
@@ -209,6 +217,7 @@ static const struct i2c_device_id lm90_id[] = {
        { "max6646", max6646 },
        { "max6647", max6646 },
        { "max6649", max6646 },
+       { "max6654", max6654 },
        { "max6657", max6657 },
        { "max6658", max6657 },
        { "max6659", max6659 },
@@ -269,6 +278,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
                .compatible = "dallas,max6649",
                .data = (void *)max6646
        },
+       {
+               .compatible = "dallas,max6654",
+               .data = (void *)max6654
+       },
        {
                .compatible = "dallas,max6657",
                .data = (void *)max6657
@@ -367,6 +380,11 @@ static const struct lm90_params lm90_params[] = {
                .max_convrate = 6,
                .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
+       [max6654] = {
+               .alert_alarms = 0x7c,
+               .max_convrate = 7,
+               .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
+       },
        [max6657] = {
                .flags = LM90_PAUSE_FOR_CONFIG,
                .alert_alarms = 0x7c,
@@ -1557,6 +1575,16 @@ static int lm90_detect(struct i2c_client *client,
                 && (config1 & 0x3f) == 0x00
                 && convrate <= 0x07) {
                        name = "max6646";
+               } else
+               /*
+                * The chip_id of the MAX6654 holds the revision of the chip.
+                * The lowest 3 bits of the config1 register are unused and
+                * should return zero when read.
+                */
+               if (chip_id == 0x08
+                && (config1 & 0x07) == 0x00
+                && convrate <= 0x07) {
+                       name = "max6654";
                }
        } else
        if (address == 0x4C
@@ -1660,6 +1688,15 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
        if (data->kind == max6680)
                config |= 0x18;
 
+       /*
+        * Put MAX6654 into extended range (0x20, extend minimum range from
+        * 0 degrees to -64 degrees). Note that extended resolution is not
+        * possible on the MAX6654 unless conversion rate is set to 1 Hz or
+        * slower, which is intentionally not done by default.
+        */
+       if (data->kind == max6654)
+               config |= 0x20;
+
        /*
         * Select external channel 0 for max6695/96
         */
index 7efa6bfef06097e5d20878566a84bc9666b3f2d6..e7e1ddc1d631b25bc7a20541458b41a291c88672 100644 (file)
@@ -2047,7 +2047,7 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
 static umode_t nct6775_in_is_visible(struct kobject *kobj,
                                     struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
        int in = index / 5;     /* voltage index */
 
@@ -2253,7 +2253,7 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr,
 static umode_t nct6775_fan_is_visible(struct kobject *kobj,
                                      struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
        int fan = index / 6;    /* fan index */
        int nr = index % 6;     /* attribute index */
@@ -2440,7 +2440,7 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
 static umode_t nct6775_temp_is_visible(struct kobject *kobj,
                                       struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
        int temp = index / 10;  /* temp index */
        int nr = index % 10;    /* attribute index */
@@ -3257,7 +3257,7 @@ store_auto_temp(struct device *dev, struct device_attribute *attr,
 static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
                                      struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
        int pwm = index / 36;   /* pwm index */
        int nr = index % 36;    /* attribute index */
@@ -3459,7 +3459,7 @@ static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep,
 static umode_t nct6775_other_is_visible(struct kobject *kobj,
                                        struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct6775_data *data = dev_get_drvdata(dev);
 
        if (index == 0 && !data->have_vid)
index 2e97e56c72c743d7b1e8d898b055f48a3bbd90d4..570df8eb52720090bf0f76ea06ce5dac5d677707 100644 (file)
@@ -679,7 +679,7 @@ static struct attribute *nct7802_temp_attrs[] = {
 static umode_t nct7802_temp_is_visible(struct kobject *kobj,
                                       struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct7802_data *data = dev_get_drvdata(dev);
        unsigned int reg;
        int err;
@@ -778,7 +778,7 @@ static struct attribute *nct7802_in_attrs[] = {
 static umode_t nct7802_in_is_visible(struct kobject *kobj,
                                     struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct7802_data *data = dev_get_drvdata(dev);
        unsigned int reg;
        int err;
@@ -853,7 +853,7 @@ static struct attribute *nct7802_fan_attrs[] = {
 static umode_t nct7802_fan_is_visible(struct kobject *kobj,
                                      struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct nct7802_data *data = dev_get_drvdata(dev);
        int fan = index / 4;    /* 4 attributes per fan */
        unsigned int reg;
index a7eb10d2a0539029bb301fb936491618e4177128..b0425694f702252e250e28e7b57aded40a5b6017 100644 (file)
@@ -8,6 +8,9 @@
  * Copyright (c) 2019 Advantech
  * Author: Amy.Shih <amy.shih@advantech.com.tw>
  *
+ * Copyright (c) 2020 Advantech
+ * Author: Yuechao Zhao <yuechao.zhao@advantech.com.cn>
+ *
  * Supports the following chips:
  *
  * Chip        #vin  #fan  #pwm  #temp  #dts  chip ID
@@ -20,6 +23,7 @@
 #include <linux/i2c.h>
 #include <linux/mutex.h>
 #include <linux/hwmon.h>
+#include <linux/watchdog.h>
 
 #define VENDOR_ID_REG          0x7A    /* Any bank */
 #define NUVOTON_ID             0x50
 #define FANCTL1_FMR_REG                0x00    /* Bank 3; 1 reg per channel */
 #define FANCTL1_OUT_REG                0x10    /* Bank 3; 1 reg per channel */
 
+#define WDT_LOCK_REG           0xE0    /* W/O Lock Watchdog Register */
+#define WDT_EN_REG             0xE1    /* R/O Watchdog Enable Register */
+#define WDT_STS_REG            0xE2    /* R/O Watchdog Status Register */
+#define WDT_TIMER_REG          0xE3    /* R/W Watchdog Timer Register */
+#define WDT_SOFT_EN            0x55    /* Enable soft watchdog timer */
+#define WDT_SOFT_DIS           0xAA    /* Disable soft watchdog timer */
+
 #define VOLT_MONITOR_MODE      0x0
 #define THERMAL_DIODE_MODE     0x1
 #define THERMISTOR_MODE                0x3
 
 #define ENABLE_TSI     BIT(1)
 
+#define WATCHDOG_TIMEOUT       1       /* 1 minute default timeout */
+
+/*The timeout range is 1-255 minutes*/
+#define MIN_TIMEOUT            (1 * 60)
+#define MAX_TIMEOUT            (255 * 60)
+
+static int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes. 1 <= timeout <= 255, default="
+                       __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                       __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
 static const unsigned short normal_i2c[] = {
        0x2d, 0x2e, I2C_CLIENT_END
 };
 
 struct nct7904_data {
        struct i2c_client *client;
+       struct watchdog_device wdt;
        struct mutex bank_lock;
        int bank_sel;
        u32 fanin_mask;
@@ -892,6 +920,95 @@ static const struct hwmon_chip_info nct7904_chip_info = {
        .info = nct7904_info,
 };
 
+/*
+ * Watchdog Function
+ */
+static int nct7904_wdt_start(struct watchdog_device *wdt)
+{
+       struct nct7904_data *data = watchdog_get_drvdata(wdt);
+
+       /* Enable soft watchdog timer */
+       return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN);
+}
+
+static int nct7904_wdt_stop(struct watchdog_device *wdt)
+{
+       struct nct7904_data *data = watchdog_get_drvdata(wdt);
+
+       return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS);
+}
+
+static int nct7904_wdt_set_timeout(struct watchdog_device *wdt,
+                                  unsigned int timeout)
+{
+       struct nct7904_data *data = watchdog_get_drvdata(wdt);
+       /*
+        * The NCT7904 is very special in watchdog function.
+        * Its minimum unit is minutes. And wdt->timeout needs
+        * to match the actual timeout selected. So, this needs
+        * to be: wdt->timeout = timeout / 60 * 60.
+        * For example, if the user configures a timeout of
+        * 119 seconds, the actual timeout will be 60 seconds.
+        * So, wdt->timeout must then be set to 60 seconds.
+        */
+       wdt->timeout = timeout / 60 * 60;
+
+       return nct7904_write_reg(data, BANK_0, WDT_TIMER_REG,
+                                wdt->timeout / 60);
+}
+
+static int nct7904_wdt_ping(struct watchdog_device *wdt)
+{
+       /*
+        * Note:
+        * NCT7904 does not support refreshing WDT_TIMER_REG register when
+        * the watchdog is active. Please disable watchdog before feeding
+        * the watchdog and enable it again.
+        */
+       struct nct7904_data *data = watchdog_get_drvdata(wdt);
+       int ret;
+
+       /* Disable soft watchdog timer */
+       ret = nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS);
+       if (ret < 0)
+               return ret;
+
+       /* feed watchdog */
+       ret = nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, wdt->timeout / 60);
+       if (ret < 0)
+               return ret;
+
+       /* Enable soft watchdog timer */
+       return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN);
+}
+
+static unsigned int nct7904_wdt_get_timeleft(struct watchdog_device *wdt)
+{
+       struct nct7904_data *data = watchdog_get_drvdata(wdt);
+       int ret;
+
+       ret = nct7904_read_reg(data, BANK_0, WDT_TIMER_REG);
+       if (ret < 0)
+               return 0;
+
+       return ret * 60;
+}
+
+static const struct watchdog_info nct7904_wdt_info = {
+       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+                               WDIOF_MAGICCLOSE,
+       .identity       = "nct7904 watchdog",
+};
+
+static const struct watchdog_ops nct7904_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = nct7904_wdt_start,
+       .stop           = nct7904_wdt_stop,
+       .ping           = nct7904_wdt_ping,
+       .set_timeout    = nct7904_wdt_set_timeout,
+       .get_timeleft   = nct7904_wdt_get_timeleft,
+};
+
 static int nct7904_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -1022,7 +1139,26 @@ static int nct7904_probe(struct i2c_client *client,
        hwmon_dev =
                devm_hwmon_device_register_with_info(dev, client->name, data,
                                                     &nct7904_chip_info, NULL);
-       return PTR_ERR_OR_ZERO(hwmon_dev);
+       ret = PTR_ERR_OR_ZERO(hwmon_dev);
+       if (ret)
+               return ret;
+
+       /* Watchdog initialization */
+       data->wdt.ops = &nct7904_wdt_ops;
+       data->wdt.info = &nct7904_wdt_info;
+
+       data->wdt.timeout = WATCHDOG_TIMEOUT * 60; /* Set default timeout */
+       data->wdt.min_timeout = MIN_TIMEOUT;
+       data->wdt.max_timeout = MAX_TIMEOUT;
+       data->wdt.parent = &client->dev;
+
+       watchdog_init_timeout(&data->wdt, timeout * 60, &client->dev);
+       watchdog_set_nowayout(&data->wdt, nowayout);
+       watchdog_set_drvdata(&data->wdt, data);
+
+       watchdog_stop_on_unregister(&data->wdt);
+
+       return devm_watchdog_register_device(dev, &data->wdt);
 }
 
 static const struct i2c_device_id nct7904_id[] = {
index de12a565006dac3a27398773babdc16f6e8c9b39..a337195b1c395e76c5a53a30a78f29315afcc8f2 100644 (file)
@@ -146,6 +146,15 @@ config SENSORS_MAX16064
          This driver can also be built as a module. If so, the module will
          be called max16064.
 
+config SENSORS_MAX16601
+       tristate "Maxim MAX16601"
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX16601.
+
+         This driver can also be built as a module. If so, the module will
+         be called max16601.
+
 config SENSORS_MAX20730
        tristate "Maxim MAX20730, MAX20734, MAX20743"
        help
index 5feb45806123e3740aa1800cd6339a5160a93d5d..c4b15db996ad63c1d3097fb7228d22295d23f11b 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
 obj-$(CONFIG_SENSORS_LTC2978)  += ltc2978.o
 obj-$(CONFIG_SENSORS_LTC3815)  += ltc3815.o
 obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
+obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
 obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
 obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
 obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
new file mode 100644 (file)
index 0000000..51cdfaf
--- /dev/null
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring driver for Maxim MAX16601
+ *
+ * Implementation notes:
+ *
+ * Ths chip supports two rails, VCORE and VSA. Telemetry information for the
+ * two rails is reported in two subsequent I2C addresses. The driver
+ * instantiates a dummy I2C client at the second I2C address to report
+ * information for the VSA rail in a single instance of the driver.
+ * Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2.
+ *
+ * The chip reports input current using two separate methods. The input current
+ * reported with the standard READ_IIN command is derived from the output
+ * current. The first method is reported to the PMBus core with PMBus page 0,
+ * the second method is reported with PMBus page 1.
+ *
+ * The chip supports reading per-phase temperatures and per-phase input/output
+ * currents for VCORE. Telemetry is reported in vendor specific registers.
+ * The driver translates the vendor specific register values to PMBus standard
+ * register values and reports per-phase information in PMBus page 0.
+ *
+ * Copyright 2019, 2020 Google LLC.
+ */
+
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "pmbus.h"
+
+#define REG_SETPT_DVID         0xd1
+#define  DAC_10MV_MODE         BIT(4)
+#define REG_IOUT_AVG_PK                0xee
+#define REG_IIN_SENSOR         0xf1
+#define REG_TOTAL_INPUT_POWER  0xf2
+#define REG_PHASE_ID           0xf3
+#define  CORE_RAIL_INDICATOR   BIT(7)
+#define REG_PHASE_REPORTING    0xf4
+
+struct max16601_data {
+       struct pmbus_driver_info info;
+       struct i2c_client *vsa;
+       int iout_avg_pkg;
+};
+
+#define to_max16601_data(x) container_of(x, struct max16601_data, info)
+
+static int max16601_read_byte(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max16601_data *data = to_max16601_data(info);
+
+       if (page > 0) {
+               if (page == 2)  /* VSA */
+                       return i2c_smbus_read_byte_data(data->vsa, reg);
+               return -EOPNOTSUPP;
+       }
+       return -ENODATA;
+}
+
+static int max16601_read_word(struct i2c_client *client, int page, int phase,
+                             int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max16601_data *data = to_max16601_data(info);
+       u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+       int ret;
+
+       switch (page) {
+       case 0:         /* VCORE */
+               if (phase == 0xff)
+                       return -ENODATA;
+               switch (reg) {
+               case PMBUS_READ_IIN:
+               case PMBUS_READ_IOUT:
+               case PMBUS_READ_TEMPERATURE_1:
+                       ret = i2c_smbus_write_byte_data(client, REG_PHASE_ID,
+                                                       phase);
+                       if (ret)
+                               return ret;
+                       ret = i2c_smbus_read_block_data(client,
+                                                       REG_PHASE_REPORTING,
+                                                       buf);
+                       if (ret < 0)
+                               return ret;
+                       if (ret < 6)
+                               return -EIO;
+                       switch (reg) {
+                       case PMBUS_READ_TEMPERATURE_1:
+                               return buf[1] << 8 | buf[0];
+                       case PMBUS_READ_IOUT:
+                               return buf[3] << 8 | buf[2];
+                       case PMBUS_READ_IIN:
+                               return buf[5] << 8 | buf[4];
+                       default:
+                               break;
+                       }
+               }
+               return -EOPNOTSUPP;
+       case 1:         /* VCORE, read IIN/PIN from sensor element */
+               switch (reg) {
+               case PMBUS_READ_IIN:
+                       return i2c_smbus_read_word_data(client, REG_IIN_SENSOR);
+               case PMBUS_READ_PIN:
+                       return i2c_smbus_read_word_data(client,
+                                                       REG_TOTAL_INPUT_POWER);
+               default:
+                       break;
+               }
+               return -EOPNOTSUPP;
+       case 2:         /* VSA */
+               switch (reg) {
+               case PMBUS_VIRT_READ_IOUT_MAX:
+                       ret = i2c_smbus_read_word_data(data->vsa,
+                                                      REG_IOUT_AVG_PK);
+                       if (ret < 0)
+                               return ret;
+                       if (sign_extend32(ret, 10) >
+                           sign_extend32(data->iout_avg_pkg, 10))
+                               data->iout_avg_pkg = ret;
+                       return data->iout_avg_pkg;
+               case PMBUS_VIRT_RESET_IOUT_HISTORY:
+                       return 0;
+               case PMBUS_IOUT_OC_FAULT_LIMIT:
+               case PMBUS_IOUT_OC_WARN_LIMIT:
+               case PMBUS_OT_FAULT_LIMIT:
+               case PMBUS_OT_WARN_LIMIT:
+               case PMBUS_READ_IIN:
+               case PMBUS_READ_IOUT:
+               case PMBUS_READ_TEMPERATURE_1:
+               case PMBUS_STATUS_WORD:
+                       return i2c_smbus_read_word_data(data->vsa, reg);
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int max16601_write_byte(struct i2c_client *client, int page, u8 reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max16601_data *data = to_max16601_data(info);
+
+       if (page == 2) {
+               if (reg == PMBUS_CLEAR_FAULTS)
+                       return i2c_smbus_write_byte(data->vsa, reg);
+               return -EOPNOTSUPP;
+       }
+       return -ENODATA;
+}
+
+static int max16601_write_word(struct i2c_client *client, int page, int reg,
+                              u16 value)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max16601_data *data = to_max16601_data(info);
+
+       switch (page) {
+       case 0:         /* VCORE */
+               return -ENODATA;
+       case 1:         /* VCORE IIN/PIN from sensor element */
+       default:
+               return -EOPNOTSUPP;
+       case 2:         /* VSA */
+               switch (reg) {
+               case PMBUS_VIRT_RESET_IOUT_HISTORY:
+                       data->iout_avg_pkg = 0xfc00;
+                       return 0;
+               case PMBUS_IOUT_OC_FAULT_LIMIT:
+               case PMBUS_IOUT_OC_WARN_LIMIT:
+               case PMBUS_OT_FAULT_LIMIT:
+               case PMBUS_OT_WARN_LIMIT:
+                       return i2c_smbus_write_word_data(data->vsa, reg, value);
+               default:
+                       return -EOPNOTSUPP;
+               }
+       }
+}
+
+static int max16601_identify(struct i2c_client *client,
+                            struct pmbus_driver_info *info)
+{
+       int reg;
+
+       reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID);
+       if (reg < 0)
+               return reg;
+       if (reg & DAC_10MV_MODE)
+               info->vrm_version[0] = vr13;
+       else
+               info->vrm_version[0] = vr12;
+
+       return 0;
+}
+
+static struct pmbus_driver_info max16601_info = {
+       .pages = 3,
+       .format[PSC_VOLTAGE_IN] = linear,
+       .format[PSC_VOLTAGE_OUT] = vid,
+       .format[PSC_CURRENT_IN] = linear,
+       .format[PSC_CURRENT_OUT] = linear,
+       .format[PSC_TEMPERATURE] = linear,
+       .format[PSC_POWER] = linear,
+       .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
+               PMBUS_HAVE_STATUS_INPUT |
+               PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+               PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+               PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
+               PMBUS_HAVE_POUT | PMBUS_PAGE_VIRTUAL | PMBUS_PHASE_VIRTUAL,
+       .func[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_PAGE_VIRTUAL,
+       .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT |
+               PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+               PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL,
+       .phases[0] = 8,
+       .pfunc[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+       .pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
+       .pfunc[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+       .pfunc[3] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
+       .pfunc[4] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+       .pfunc[5] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
+       .pfunc[6] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+       .pfunc[7] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
+       .identify = max16601_identify,
+       .read_byte_data = max16601_read_byte,
+       .read_word_data = max16601_read_word,
+       .write_byte = max16601_write_byte,
+       .write_word_data = max16601_write_word,
+};
+
+static void max16601_remove(void *_data)
+{
+       struct max16601_data *data = _data;
+
+       i2c_unregister_device(data->vsa);
+}
+
+static int max16601_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+       struct max16601_data *data;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+               return -ENODEV;
+
+       ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
+       if (ret < 0)
+               return -ENODEV;
+
+       /* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" */
+       if (ret < 11 || strncmp(buf, "MAX16601", 8)) {
+               buf[ret] = '\0';
+               dev_err(dev, "Unsupported chip '%s'\n", buf);
+               return -ENODEV;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID);
+       if (ret < 0)
+               return ret;
+       if (!(ret & CORE_RAIL_INDICATOR)) {
+               dev_err(dev,
+                       "Driver must be instantiated on CORE rail I2C address\n");
+               return -ENODEV;
+       }
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->iout_avg_pkg = 0xfc00;
+       data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1);
+       if (IS_ERR(data->vsa)) {
+               dev_err(dev, "Failed to register VSA client\n");
+               return PTR_ERR(data->vsa);
+       }
+       ret = devm_add_action_or_reset(dev, max16601_remove, data);
+       if (ret)
+               return ret;
+
+       data->info = max16601_info;
+
+       return pmbus_do_probe(client, id, &data->info);
+}
+
+static const struct i2c_device_id max16601_id[] = {
+       {"max16601", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max16601_id);
+
+static struct i2c_driver max16601_driver = {
+       .driver = {
+                  .name = "max16601",
+                  },
+       .probe = max16601_probe,
+       .remove = pmbus_do_remove,
+       .id_table = max16601_id,
+};
+
+module_i2c_driver(max16601_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601");
+MODULE_LICENSE("GPL v2");
index 8d321bf7d15bf4f9018ba776c045b63a6ce2eb16..a420877ba5335c4bf546dd020fdf7623083fa143 100644 (file)
@@ -109,8 +109,8 @@ struct pmbus_data {
        bool has_status_word;           /* device uses STATUS_WORD register */
        int (*read_status)(struct i2c_client *client, int page);
 
-       u8 currpage;
-       u8 currphase;   /* current phase, 0xff for all */
+       s16 currpage;   /* current page, -1 for unknown/unset */
+       s16 currphase;  /* current phase, 0xff for all, -1 for unknown/unset */
 };
 
 struct pmbus_debugfs_entry {
@@ -2529,8 +2529,8 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
        if (pdata)
                data->flags = pdata->flags;
        data->info = info;
-       data->currpage = 0xff;
-       data->currphase = 0xfe;
+       data->currpage = -1;
+       data->currphase = -1;
 
        ret = pmbus_init_common(client, data, info);
        if (ret < 0)
index 83e841be108190e346844923d43dfa73069ae3d3..02dbb5ca3bcf8f78860cf67198f7704a3392d10f 100644 (file)
@@ -107,7 +107,7 @@ config CORESIGHT_CPU_DEBUG
          can quickly get to know program counter (PC), secure state,
          exception level, etc. Before use debugging functionality, platform
          needs to ensure the clock domain and power domain are enabled
-         properly, please refer Documentation/trace/coresight-cpu-debug.rst
+         properly, please refer Documentation/trace/coresight/coresight-cpu-debug.rst
          for detailed description and the example for usage.
 
 config CORESIGHT_CTI
index 5536673060cc637d505bcc15cee1c1127e721118..c429d664f655563414374dcf26e541bdefe1a5e8 100644 (file)
@@ -357,12 +357,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
        if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
                dev_pm_set_driver_flags(&pdev->dev,
                                        DPM_FLAG_SMART_PREPARE |
-                                       DPM_FLAG_LEAVE_SUSPENDED);
+                                       DPM_FLAG_MAY_SKIP_RESUME);
        } else {
                dev_pm_set_driver_flags(&pdev->dev,
                                        DPM_FLAG_SMART_PREPARE |
                                        DPM_FLAG_SMART_SUSPEND |
-                                       DPM_FLAG_LEAVE_SUSPENDED);
+                                       DPM_FLAG_MAY_SKIP_RESUME);
        }
 
        /* The code below assumes runtime PM to be disabled. */
index d79cd6d54b3a7c1688885b3d68a5c6eb65da683a..97f2e29265da789220cc727fd2990a0252d08b89 100644 (file)
@@ -1008,7 +1008,6 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master,
                                    struct i3c_device_info *info)
 {
        struct i3c_ccc_cmd_dest dest;
-       unsigned int expected_len;
        struct i3c_ccc_mrl *mrl;
        struct i3c_ccc_cmd cmd;
        int ret;
@@ -1024,22 +1023,23 @@ static int i3c_master_getmrl_locked(struct i3c_master_controller *master,
        if (!(info->bcr & I3C_BCR_IBI_PAYLOAD))
                dest.payload.len -= 1;
 
-       expected_len = dest.payload.len;
        i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMRL, &dest, 1);
        ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
        if (ret)
                goto out;
 
-       if (dest.payload.len != expected_len) {
+       switch (dest.payload.len) {
+       case 3:
+               info->max_ibi_len = mrl->ibi_len;
+               fallthrough;
+       case 2:
+               info->max_read_len = be16_to_cpu(mrl->read_len);
+               break;
+       default:
                ret = -EIO;
                goto out;
        }
 
-       info->max_read_len = be16_to_cpu(mrl->read_len);
-
-       if (info->bcr & I3C_BCR_IBI_PAYLOAD)
-               info->max_ibi_len = mrl->ibi_len;
-
 out:
        i3c_ccc_cmd_dest_cleanup(&dest);
 
index 4fb42b743f0f2dbf11e2d67da159a87823773060..7468fbd11684357ec64e0543a5a6e683eed1fce3 100644 (file)
@@ -3,7 +3,7 @@
  * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
  *
  * Copyright 2016 Qtechnology A/S
- * 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ * 2016 Ricardo Ribalda <ribalda@kernel.org>
  */
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -423,6 +423,6 @@ static struct spi_driver ad5761_driver = {
 };
 module_spi_driver(ad5761_driver);
 
-MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
+MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
 MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
 MODULE_LICENSE("GPL v2");
index c46805144dd4f3b0b33b8deeccabbbd06ca31f01..de0c6573cd9790d8c57ef383ee68ea6af9f3bc65 100644 (file)
@@ -3,7 +3,7 @@
  * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
  *
  * Copyright 2019 Qtechnology A/S
- * 2019 Ricardo Ribalda <ricardo@ribalda.com>
+ * 2019 Ricardo Ribalda <ribalda@kernel.org>
  *
  * Licensed under the GPL-2.
  */
@@ -179,6 +179,6 @@ static struct spi_driver dac7612_driver = {
 };
 module_spi_driver(dac7612_driver);
 
-MODULE_AUTHOR("Ricardo Ribalda <ricardo@ribalda.com>");
+MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
 MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
 MODULE_LICENSE("GPL v2");
index bf8e149d31911a3463f5bf7abd704a44afaec031..e0a5e897e4b1df04425421994e6a3673271d6121 100644 (file)
@@ -153,9 +153,9 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj,
        uobj->context = NULL;
 
        /*
-        * For DESTROY the usecnt is held write locked, the caller is expected
-        * to put it unlock and put the object when done with it. Only DESTROY
-        * can remove the IDR handle.
+        * For DESTROY the usecnt is not changed, the caller is expected to
+        * manage it via uobj_put_destroy(). Only DESTROY can remove the IDR
+        * handle.
         */
        if (reason != RDMA_REMOVE_DESTROY)
                atomic_set(&uobj->usecnt, 0);
@@ -187,7 +187,7 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj,
 /*
  * This calls uverbs_destroy_uobject() using the RDMA_REMOVE_DESTROY
  * sequence. It should only be used from command callbacks. On success the
- * caller must pair this with rdma_lookup_put_uobject(LOOKUP_WRITE). This
+ * caller must pair this with uobj_put_destroy(). This
  * version requires the caller to have already obtained an
  * LOOKUP_DESTROY uobject kref.
  */
@@ -198,6 +198,13 @@ int uobj_destroy(struct ib_uobject *uobj, struct uverbs_attr_bundle *attrs)
 
        down_read(&ufile->hw_destroy_rwsem);
 
+       /*
+        * Once the uobject is destroyed by RDMA_REMOVE_DESTROY then it is left
+        * write locked as the callers put it back with UVERBS_LOOKUP_DESTROY.
+        * This is because any other concurrent thread can still see the object
+        * in the xarray due to RCU. Leaving it locked ensures nothing else will
+        * touch it.
+        */
        ret = uverbs_try_lock_object(uobj, UVERBS_LOOKUP_WRITE);
        if (ret)
                goto out_unlock;
@@ -216,7 +223,7 @@ out_unlock:
 /*
  * uobj_get_destroy destroys the HW object and returns a handle to the uobj
  * with a NULL object pointer. The caller must pair this with
- * uverbs_put_destroy.
+ * uobj_put_destroy().
  */
 struct ib_uobject *__uobj_get_destroy(const struct uverbs_api_object *obj,
                                      u32 id, struct uverbs_attr_bundle *attrs)
@@ -250,8 +257,7 @@ int __uobj_perform_destroy(const struct uverbs_api_object *obj, u32 id,
        uobj = __uobj_get_destroy(obj, id, attrs);
        if (IS_ERR(uobj))
                return PTR_ERR(uobj);
-
-       rdma_lookup_put_uobject(uobj, UVERBS_LOOKUP_WRITE);
+       uobj_put_destroy(uobj);
        return 0;
 }
 
index 4da03f82347492001c5e0fb49af5393087be9b4b..f81ca20f4b693e1597fc1cd10d62a21acdf05a2f 100644 (file)
@@ -206,13 +206,6 @@ static int pin_rcv_pages(struct hfi1_filedata *fd, struct tid_user_buf *tidbuf)
                return -EINVAL;
        }
 
-       /* Verify that access is OK for the user buffer */
-       if (!access_ok((void __user *)vaddr,
-                      npages * PAGE_SIZE)) {
-               dd_dev_err(dd, "Fail vaddr %p, %u pages, !access_ok\n",
-                          (void *)vaddr, npages);
-               return -EFAULT;
-       }
        /* Allocate the array of struct page pointers needed for pinning */
        pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL);
        if (!pages)
index a401931189b75b13e3a37ea5d739f36134bc9a09..44683073be0c4a7988d6f3eb917936b444470de1 100644 (file)
@@ -1439,6 +1439,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
 
        if (is_odp_mr(mr)) {
                to_ib_umem_odp(mr->umem)->private = mr;
+               init_waitqueue_head(&mr->q_deferred_work);
                atomic_set(&mr->num_deferred_work, 0);
                err = xa_err(xa_store(&dev->odp_mkeys,
                                      mlx5_base_mkey(mr->mmkey.key), &mr->mmkey,
index 568b21eb6ea158508207275f2626e1c46cbbd892..021df0654ba757e1df63d88d89a92035f072f3ab 100644 (file)
@@ -760,7 +760,7 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
                qib_dev_err(dd,
                        "Skipping linkcontrol sysfs info, (err %d) port %u\n",
                        ret, port_num);
-               goto bail;
+               goto bail_link;
        }
        kobject_uevent(&ppd->pport_kobj, KOBJ_ADD);
 
@@ -770,7 +770,7 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
                qib_dev_err(dd,
                        "Skipping sl2vl sysfs info, (err %d) port %u\n",
                        ret, port_num);
-               goto bail_link;
+               goto bail_sl;
        }
        kobject_uevent(&ppd->sl2vl_kobj, KOBJ_ADD);
 
@@ -780,7 +780,7 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
                qib_dev_err(dd,
                        "Skipping diag_counters sysfs info, (err %d) port %u\n",
                        ret, port_num);
-               goto bail_sl;
+               goto bail_diagc;
        }
        kobject_uevent(&ppd->diagc_kobj, KOBJ_ADD);
 
@@ -793,7 +793,7 @@ int qib_create_port_files(struct ib_device *ibdev, u8 port_num,
                qib_dev_err(dd,
                 "Skipping Congestion Control sysfs info, (err %d) port %u\n",
                 ret, port_num);
-               goto bail_diagc;
+               goto bail_cc;
        }
 
        kobject_uevent(&ppd->pport_cc_kobj, KOBJ_ADD);
@@ -854,6 +854,7 @@ void qib_verbs_unregister_sysfs(struct qib_devdata *dd)
                                &cc_table_bin_attr);
                        kobject_put(&ppd->pport_cc_kobj);
                }
+               kobject_put(&ppd->diagc_kobj);
                kobject_put(&ppd->sl2vl_kobj);
                kobject_put(&ppd->pport_kobj);
        }
index e580ae9cc55a52143362f512e369d7717beaffe3..780fd2dfc07eb03200fede1ad914ab3900af2c87 100644 (file)
@@ -829,7 +829,7 @@ static int pvrdma_pci_probe(struct pci_dev *pdev,
            !(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
                dev_err(&pdev->dev, "PCI BAR region not MMIO\n");
                ret = -ENOMEM;
-               goto err_free_device;
+               goto err_disable_pdev;
        }
 
        ret = pci_request_regions(pdev, DRV_NAME);
index e188a95984b5c3c38800dc8b061494be874758c8..9a3379c49541fbe51a08e49db7757cc170bdd1bf 100644 (file)
@@ -377,8 +377,12 @@ struct ipoib_dev_priv {
        struct ipoib_rx_buf *rx_ring;
 
        struct ipoib_tx_buf *tx_ring;
+       /* cyclic ring variables for managing tx_ring, for UD only */
        unsigned int         tx_head;
        unsigned int         tx_tail;
+       /* cyclic ring variables for counting overall outstanding send WRs */
+       unsigned int         global_tx_head;
+       unsigned int         global_tx_tail;
        struct ib_sge        tx_sge[MAX_SKB_FRAGS + 1];
        struct ib_ud_wr      tx_wr;
        struct ib_wc         send_wc[MAX_SEND_CQE];
index c59e00a0881f19e6efae40145332743de8407ca7..9bf0fa30df28c9f76480bfcdf2b20846fe409864 100644 (file)
@@ -756,7 +756,8 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_
                return;
        }
 
-       if ((priv->tx_head - priv->tx_tail) == ipoib_sendq_size - 1) {
+       if ((priv->global_tx_head - priv->global_tx_tail) ==
+           ipoib_sendq_size - 1) {
                ipoib_dbg(priv, "TX ring 0x%x full, stopping kernel net queue\n",
                          tx->qp->qp_num);
                netif_stop_queue(dev);
@@ -786,7 +787,7 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_
        } else {
                netif_trans_update(dev);
                ++tx->tx_head;
-               ++priv->tx_head;
+               ++priv->global_tx_head;
        }
 }
 
@@ -820,10 +821,11 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
        netif_tx_lock(dev);
 
        ++tx->tx_tail;
-       ++priv->tx_tail;
+       ++priv->global_tx_tail;
 
        if (unlikely(netif_queue_stopped(dev) &&
-                    (priv->tx_head - priv->tx_tail) <= ipoib_sendq_size >> 1 &&
+                    ((priv->global_tx_head - priv->global_tx_tail) <=
+                     ipoib_sendq_size >> 1) &&
                     test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)))
                netif_wake_queue(dev);
 
@@ -1232,8 +1234,9 @@ timeout:
                dev_kfree_skb_any(tx_req->skb);
                netif_tx_lock_bh(p->dev);
                ++p->tx_tail;
-               ++priv->tx_tail;
-               if (unlikely(priv->tx_head - priv->tx_tail == ipoib_sendq_size >> 1) &&
+               ++priv->global_tx_tail;
+               if (unlikely((priv->global_tx_head - priv->global_tx_tail) <=
+                            ipoib_sendq_size >> 1) &&
                    netif_queue_stopped(p->dev) &&
                    test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
                        netif_wake_queue(p->dev);
index c332b47618160327966b493a7493e7006833d345..da3c5315bbb515649c7f265fe263f38cca5f9d6e 100644 (file)
@@ -407,9 +407,11 @@ static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
        dev_kfree_skb_any(tx_req->skb);
 
        ++priv->tx_tail;
+       ++priv->global_tx_tail;
 
        if (unlikely(netif_queue_stopped(dev) &&
-                    ((priv->tx_head - priv->tx_tail) <= ipoib_sendq_size >> 1) &&
+                    ((priv->global_tx_head - priv->global_tx_tail) <=
+                     ipoib_sendq_size >> 1) &&
                     test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)))
                netif_wake_queue(dev);
 
@@ -634,7 +636,8 @@ int ipoib_send(struct net_device *dev, struct sk_buff *skb,
        else
                priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;
        /* increase the tx_head after send success, but use it for queue state */
-       if (priv->tx_head - priv->tx_tail == ipoib_sendq_size - 1) {
+       if ((priv->global_tx_head - priv->global_tx_tail) ==
+           ipoib_sendq_size - 1) {
                ipoib_dbg(priv, "TX ring full, stopping kernel net queue\n");
                netif_stop_queue(dev);
        }
@@ -662,6 +665,7 @@ int ipoib_send(struct net_device *dev, struct sk_buff *skb,
 
                rc = priv->tx_head;
                ++priv->tx_head;
+               ++priv->global_tx_head;
        }
        return rc;
 }
@@ -807,6 +811,7 @@ int ipoib_ib_dev_stop_default(struct net_device *dev)
                                ipoib_dma_unmap_tx(priv, tx_req);
                                dev_kfree_skb_any(tx_req->skb);
                                ++priv->tx_tail;
+                               ++priv->global_tx_tail;
                        }
 
                        for (i = 0; i < ipoib_recvq_size; ++i) {
index 81b8227214f1cf11ee622d16d3876e0eae7ed4d9..ceec24d451858adaac8c8503c5ccccb79c6a373c 100644 (file)
@@ -1184,9 +1184,11 @@ static void ipoib_timeout(struct net_device *dev, unsigned int txqueue)
 
        ipoib_warn(priv, "transmit timeout: latency %d msecs\n",
                   jiffies_to_msecs(jiffies - dev_trans_start(dev)));
-       ipoib_warn(priv, "queue stopped %d, tx_head %u, tx_tail %u\n",
-                  netif_queue_stopped(dev),
-                  priv->tx_head, priv->tx_tail);
+       ipoib_warn(priv,
+                  "queue stopped %d, tx_head %u, tx_tail %u, global_tx_head %u, global_tx_tail %u\n",
+                  netif_queue_stopped(dev), priv->tx_head, priv->tx_tail,
+                  priv->global_tx_head, priv->global_tx_tail);
+
        /* XXX reset QP, etc. */
 }
 
@@ -1701,7 +1703,7 @@ static int ipoib_dev_init_default(struct net_device *dev)
                goto out_rx_ring_cleanup;
        }
 
-       /* priv->tx_head, tx_tail & tx_outstanding are already 0 */
+       /* priv->tx_head, tx_tail and global_tx_tail/head are already 0 */
 
        if (ipoib_transport_dev_init(dev, priv->ca)) {
                pr_warn("%s: ipoib_transport_dev_init failed\n",
index cb6e3a5f509c8a06ad03fc5bdf26d137189cbb9b..0d57e51b8ba1f8640063b043a4d1007e6376ab68 100644 (file)
@@ -326,20 +326,6 @@ static int evdev_fasync(int fd, struct file *file, int on)
        return fasync_helper(fd, file, on, &client->fasync);
 }
 
-static int evdev_flush(struct file *file, fl_owner_t id)
-{
-       struct evdev_client *client = file->private_data;
-       struct evdev *evdev = client->evdev;
-
-       mutex_lock(&evdev->mutex);
-
-       if (evdev->exist && !client->revoked)
-               input_flush_device(&evdev->handle, file);
-
-       mutex_unlock(&evdev->mutex);
-       return 0;
-}
-
 static void evdev_free(struct device *dev)
 {
        struct evdev *evdev = container_of(dev, struct evdev, dev);
@@ -453,6 +439,10 @@ static int evdev_release(struct inode *inode, struct file *file)
        unsigned int i;
 
        mutex_lock(&evdev->mutex);
+
+       if (evdev->exist && !client->revoked)
+               input_flush_device(&evdev->handle, file);
+
        evdev_ungrab(evdev, client);
        mutex_unlock(&evdev->mutex);
 
@@ -1310,7 +1300,6 @@ static const struct file_operations evdev_fops = {
        .compat_ioctl   = evdev_ioctl_compat,
 #endif
        .fasync         = evdev_fasync,
-       .flush          = evdev_flush,
        .llseek         = no_llseek,
 };
 
index 6b40a1c68f9fe40eeb12a62ac40406885bcbc6ff..c77cdb3b62b5b4c7ed5ab4905bcdcf59ca761dec 100644 (file)
@@ -458,6 +458,16 @@ static const u8 xboxone_fw2015_init[] = {
        0x05, 0x20, 0x00, 0x01, 0x00
 };
 
+/*
+ * This packet is required for Xbox One S (0x045e:0x02ea)
+ * and Xbox One Elite Series 2 (0x045e:0x0b00) pads to
+ * initialize the controller that was previously used in
+ * Bluetooth mode.
+ */
+static const u8 xboxone_s_init[] = {
+       0x05, 0x20, 0x00, 0x0f, 0x06
+};
+
 /*
  * This packet is required for the Titanfall 2 Xbox One pads
  * (0x0e6f:0x0165) to finish initialization and for Hori pads
@@ -516,6 +526,8 @@ static const struct xboxone_init_packet xboxone_init_packets[] = {
        XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
        XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
        XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
+       XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
+       XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
        XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
        XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
        XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
index d38398526965dbfdeedcffdd58c016c123fa53b1..14362ebab9a9d8bcc1b6be958b1c02b3bcb8a46d 100644 (file)
@@ -186,7 +186,7 @@ struct touchpad_protocol {
        u8                      number_of_fingers;
        u8                      clicked2;
        u8                      unknown3[16];
-       struct tp_finger        fingers[0];
+       struct tp_finger        fingers[];
 };
 
 /**
index 2b71c5a51f907e338e1a785833967e0e5d24b969..fc1793ca2f1747511c04d40b79e2e3790fe39221 100644 (file)
@@ -347,18 +347,14 @@ static int cros_ec_keyb_info(struct cros_ec_device *ec_dev,
        params->info_type = info_type;
        params->event_type = event_type;
 
-       ret = cros_ec_cmd_xfer(ec_dev, msg);
-       if (ret < 0) {
-               dev_warn(ec_dev->dev, "Transfer error %d/%d: %d\n",
-                        (int)info_type, (int)event_type, ret);
-       } else if (msg->result == EC_RES_INVALID_VERSION) {
+       ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+       if (ret == -ENOTSUPP) {
                /* With older ECs we just return 0 for everything */
                memset(result, 0, result_size);
                ret = 0;
-       } else if (msg->result != EC_RES_SUCCESS) {
-               dev_warn(ec_dev->dev, "Error getting info %d/%d: %d\n",
-                        (int)info_type, (int)event_type, msg->result);
-               ret = -EPROTO;
+       } else if (ret < 0) {
+               dev_warn(ec_dev->dev, "Transfer error %d/%d: %d\n",
+                        (int)info_type, (int)event_type, ret);
        } else if (ret != result_size) {
                dev_warn(ec_dev->dev, "Wrong size %d/%d: %d != %zu\n",
                         (int)info_type, (int)event_type,
index b0ead7199c405a0e702ee478bfde94f0c6f433b0..a69dcc3bd30c79673f08e1763c7485bd34b80cfc 100644 (file)
@@ -143,7 +143,7 @@ MODULE_DEVICE_TABLE(of, dir685_tk_of_match);
 
 static struct i2c_driver dir685_tk_i2c_driver = {
        .driver = {
-               .name   = "dlin-dir685-touchkeys",
+               .name   = "dlink-dir685-touchkeys",
                .of_match_table = of_match_ptr(dir685_tk_of_match),
        },
        .probe          = dir685_tk_probe,
index c8f87df93a50e3dae6673921baf4707daf1eb240..9c6386b2af331b1696def8535d6adb6b82b98c27 100644 (file)
@@ -205,8 +205,11 @@ ATTRIBUTE_GROUPS(axp20x);
 
 static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
 {
-       struct input_dev *idev = pwr;
-       struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
+       struct axp20x_pek *axp20x_pek = pwr;
+       struct input_dev *idev = axp20x_pek->input;
+
+       if (!idev)
+               return IRQ_HANDLED;
 
        /*
         * The power-button is connected to ground so a falling edge (dbf)
@@ -225,22 +228,9 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
 static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
                                         struct platform_device *pdev)
 {
-       struct axp20x_dev *axp20x = axp20x_pek->axp20x;
        struct input_dev *idev;
        int error;
 
-       axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
-       if (axp20x_pek->irq_dbr < 0)
-               return axp20x_pek->irq_dbr;
-       axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
-                                                 axp20x_pek->irq_dbr);
-
-       axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
-       if (axp20x_pek->irq_dbf < 0)
-               return axp20x_pek->irq_dbf;
-       axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
-                                                 axp20x_pek->irq_dbf);
-
        axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
        if (!axp20x_pek->input)
                return -ENOMEM;
@@ -255,24 +245,6 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
 
        input_set_drvdata(idev, axp20x_pek);
 
-       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
-                                            axp20x_pek_irq, 0,
-                                            "axp20x-pek-dbr", idev);
-       if (error < 0) {
-               dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
-                       axp20x_pek->irq_dbr, error);
-               return error;
-       }
-
-       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
-                                         axp20x_pek_irq, 0,
-                                         "axp20x-pek-dbf", idev);
-       if (error < 0) {
-               dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
-                       axp20x_pek->irq_dbf, error);
-               return error;
-       }
-
        error = input_register_device(idev);
        if (error) {
                dev_err(&pdev->dev, "Can't register input device: %d\n",
@@ -280,8 +252,6 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
                return error;
        }
 
-       device_init_wakeup(&pdev->dev, true);
-
        return 0;
 }
 
@@ -339,6 +309,18 @@ static int axp20x_pek_probe(struct platform_device *pdev)
 
        axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
 
+       axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
+       if (axp20x_pek->irq_dbr < 0)
+               return axp20x_pek->irq_dbr;
+       axp20x_pek->irq_dbr = regmap_irq_get_virq(
+                       axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbr);
+
+       axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
+       if (axp20x_pek->irq_dbf < 0)
+               return axp20x_pek->irq_dbf;
+       axp20x_pek->irq_dbf = regmap_irq_get_virq(
+                       axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbf);
+
        if (axp20x_pek_should_register_input(axp20x_pek, pdev)) {
                error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
                if (error)
@@ -347,6 +329,26 @@ static int axp20x_pek_probe(struct platform_device *pdev)
 
        axp20x_pek->info = (struct axp20x_info *)match->driver_data;
 
+       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
+                                            axp20x_pek_irq, 0,
+                                            "axp20x-pek-dbr", axp20x_pek);
+       if (error < 0) {
+               dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
+                       axp20x_pek->irq_dbr, error);
+               return error;
+       }
+
+       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
+                                         axp20x_pek_irq, 0,
+                                         "axp20x-pek-dbf", axp20x_pek);
+       if (error < 0) {
+               dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
+                       axp20x_pek->irq_dbf, error);
+               return error;
+       }
+
+       device_init_wakeup(&pdev->dev, true);
+
        platform_set_drvdata(pdev, axp20x_pek);
 
        return 0;
index 4d2036209b45d9391828daeeeea5d583729a821e..758dae8d650066006189ef429c589ddedf87bf27 100644 (file)
@@ -170,6 +170,7 @@ static const char * const smbus_pnp_ids[] = {
        "LEN005b", /* P50 */
        "LEN005e", /* T560 */
        "LEN006c", /* T470s */
+       "LEN007a", /* T470s */
        "LEN0071", /* T480 */
        "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
        "LEN0073", /* X1 Carbon G5 (Elantech) */
index 190b9974526bb9e78f8b3200283b680690db2079..258d5fe3d395c4670088aa0d736cac69c7d24550 100644 (file)
@@ -205,7 +205,7 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
 
        if (count) {
                kfree(attn_data.data);
-               attn_data.data = NULL;
+               drvdata->attn_data.data = NULL;
        }
 
        if (!kfifo_is_empty(&drvdata->attn_fifo))
@@ -1210,7 +1210,8 @@ static int rmi_driver_probe(struct device *dev)
        if (data->input) {
                rmi_driver_set_input_name(rmi_dev, data->input);
                if (!rmi_dev->xport->input) {
-                       if (input_register_device(data->input)) {
+                       retval = input_register_device(data->input);
+                       if (retval) {
                                dev_err(dev, "%s: Failed to register input device.\n",
                                        __func__);
                                goto err_destroy_functions;
index 08e919dbeb5d1f29d572b3970ab53f876623acdf..7e048b55746241de9e9d53021a87624282081735 100644 (file)
@@ -662,6 +662,13 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"),
                },
        },
+       {
+               /* Lenovo ThinkPad Twist S230u */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"),
+               },
+       },
        { }
 };
 
index 14c577c16b169ff15f686fa8fd5c49e4007e73cd..2289f9638116c3beb93be701069561331852a800 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 
+#include <linux/bits.h>
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
@@ -73,6 +74,7 @@
 #define FW_POS_STATE           1
 #define FW_POS_TOTAL           2
 #define FW_POS_XY              3
+#define FW_POS_TOOL_TYPE       33
 #define FW_POS_CHECKSUM                34
 #define FW_POS_WIDTH           35
 #define FW_POS_PRESSURE                45
@@ -842,6 +844,7 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
 {
        struct input_dev *input = ts->input;
        unsigned int n_fingers;
+       unsigned int tool_type;
        u16 finger_state;
        int i;
 
@@ -852,6 +855,10 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
        dev_dbg(&ts->client->dev,
                "n_fingers: %u, state: %04x\n",  n_fingers, finger_state);
 
+       /* Note: all fingers have the same tool type */
+       tool_type = buf[FW_POS_TOOL_TYPE] & BIT(0) ?
+                       MT_TOOL_FINGER : MT_TOOL_PALM;
+
        for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) {
                if (finger_state & 1) {
                        unsigned int x, y, p, w;
@@ -867,7 +874,7 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
                                i, x, y, p, w);
 
                        input_mt_slot(input, i);
-                       input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+                       input_mt_report_slot_state(input, tool_type, true);
                        input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
                        input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
                        input_event(input, EV_ABS, ABS_MT_PRESSURE, p);
@@ -1307,6 +1314,8 @@ static int elants_i2c_probe(struct i2c_client *client,
        input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0);
        input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
        input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+       input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE,
+                            0, MT_TOOL_PALM, 0, 0);
        input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
        input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
        input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, 1);
index 69c6d559eeb0056aef5afc4df6473760b6036428..2ef1adaed9afb65201dcc2cdfe05103474bcbc8e 100644 (file)
@@ -91,15 +91,15 @@ static int __mms114_read_reg(struct mms114_data *data, unsigned int reg,
        if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)
                BUG();
 
-       /* Write register: use repeated start */
+       /* Write register */
        xfer[0].addr = client->addr;
-       xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART;
+       xfer[0].flags = client->flags & I2C_M_TEN;
        xfer[0].len = 1;
        xfer[0].buf = &buf;
 
        /* Read data */
        xfer[1].addr = client->addr;
-       xfer[1].flags = I2C_M_RD;
+       xfer[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
        xfer[1].len = len;
        xfer[1].buf = val;
 
@@ -428,10 +428,8 @@ static int mms114_probe(struct i2c_client *client,
        const void *match_data;
        int error;
 
-       if (!i2c_check_functionality(client->adapter,
-                               I2C_FUNC_PROTOCOL_MANGLING)) {
-               dev_err(&client->dev,
-                       "Need i2c bus that supports protocol mangling\n");
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "Not supported I2C adapter\n");
                return -ENODEV;
        }
 
index 16d70201de4a3334fc4dd49dc3d205ce4d96bc52..397cb1d3f481baf16e1f298dae7653d1c83b7c54 100644 (file)
@@ -182,6 +182,7 @@ static const struct usb_device_id usbtouch_devices[] = {
 #endif
 
 #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
+       {USB_DEVICE(0x255e, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
        {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
        {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
        {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
index 1faa08c8bbb4dd3f39853fc357d49c4ac9c7364c..03d6a26687bc45ec6aac98ae5181f1a70573c454 100644 (file)
@@ -510,7 +510,7 @@ struct iommu_group *iommu_group_alloc(void)
                                   NULL, "%d", group->id);
        if (ret) {
                ida_simple_remove(&iommu_group_ida, group->id);
-               kfree(group);
+               kobject_put(&group->kobj);
                return ERR_PTR(ret);
        }
 
index 66cdc003b8f42cff8f15ba4634266a169e76150f..d288acbc99c7c29c19c5bec1e312fa8cad16ca57 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright 2013 Qtechnology/AS
  *
  * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
- * Author: Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ * Author: Ricardo Ribalda <ribalda@kernel.org>
  *
  * Based on leds-pca955x.c
  *
index 9a967a2e83dd72711db1ab538c650f126cafb97d..6e677ff62cc9696d0a73e7a1208fae960cfc54cc 100644 (file)
@@ -145,9 +145,8 @@ static int pblk_l2p_init(struct pblk *pblk, bool factory_init)
        int ret = 0;
 
        map_size = pblk_trans_map_size(pblk);
-       pblk->trans_map = __vmalloc(map_size, GFP_KERNEL | __GFP_NOWARN
-                                       | __GFP_RETRY_MAYFAIL | __GFP_HIGHMEM,
-                                       PAGE_KERNEL);
+       pblk->trans_map = __vmalloc(map_size, GFP_KERNEL | __GFP_NOWARN |
+                                   __GFP_RETRY_MAYFAIL | __GFP_HIGHMEM);
        if (!pblk->trans_map) {
                pblk_err(pblk, "failed to allocate L2P (need %zu of memory)\n",
                                map_size);
index 83eb05bf85ff936c6d805562fc97f20f69b70e23..8450d7c008d0fc249502e277739c3df11d07cbf8 100644 (file)
@@ -2184,8 +2184,6 @@ pmu_read(struct file *file, char __user *buf,
 
        if (count < 1 || !pp)
                return -EINVAL;
-       if (!access_ok(buf, count))
-               return -EFAULT;
 
        spin_lock_irqsave(&pp->lock, flags);
        add_wait_queue(&pp->wait, &wait);
index 2d519c2235626e4237a074e11d6e03d942914602..d1786cfd7f2276c6bad8a34366dd4479626bafe7 100644 (file)
@@ -400,13 +400,13 @@ static void *alloc_buffer_data(struct dm_bufio_client *c, gfp_t gfp_mask,
         */
        if (gfp_mask & __GFP_NORETRY) {
                unsigned noio_flag = memalloc_noio_save();
-               void *ptr = __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+               void *ptr = __vmalloc(c->block_size, gfp_mask);
 
                memalloc_noio_restore(noio_flag);
                return ptr;
        }
 
-       return __vmalloc(c->block_size, gfp_mask, PAGE_KERNEL);
+       return __vmalloc(c->block_size, gfp_mask);
 }
 
 /*
index b952bd45bd6a321861278842b203e3eb4e22dcc9..95a5f3757fa30854ab46227a1bc92b413dfd8e10 100644 (file)
@@ -324,14 +324,6 @@ static void end_bitmap_write(struct buffer_head *bh, int uptodate)
                wake_up(&bitmap->write_wait);
 }
 
-/* copied from buffer.c */
-static void
-__clear_page_buffers(struct page *page)
-{
-       ClearPagePrivate(page);
-       set_page_private(page, 0);
-       put_page(page);
-}
 static void free_buffers(struct page *page)
 {
        struct buffer_head *bh;
@@ -345,7 +337,7 @@ static void free_buffers(struct page *page)
                free_buffer_head(bh);
                bh = next;
        }
-       __clear_page_buffers(page);
+       detach_page_private(page);
        put_page(page);
 }
 
@@ -374,7 +366,7 @@ static int read_page(struct file *file, unsigned long index,
                ret = -ENOMEM;
                goto out;
        }
-       attach_page_buffers(page, bh);
+       attach_page_private(page, bh);
        blk_cur = index << (PAGE_SHIFT - inode->i_blkbits);
        while (bh) {
                block = blk_cur;
index e748cd54b45da0b8e6ecda47125c08bd43900dbd..517e0035fc99367baa78bd137c18b0e79f13b971 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * cec-notifier.c - notify CEC drivers of physical address changes
  *
- * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
+ * Copyright 2016 Russell King.
  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  */
 
index 6db60e9d518313f736503c058ddbd7ac65400292..92072a08af25502d539b20b4b247b69618bd39b1 100644 (file)
@@ -309,8 +309,7 @@ static void *vb2_dma_sg_vaddr(void *buf_priv)
                if (buf->db_attach)
                        buf->vaddr = dma_buf_vmap(buf->db_attach->dmabuf);
                else
-                       buf->vaddr = vm_map_ram(buf->pages,
-                                       buf->num_pages, -1, PAGE_KERNEL);
+                       buf->vaddr = vm_map_ram(buf->pages, buf->num_pages, -1);
        }
 
        /* add offset in case userptr is not page-aligned */
index 1a4f0ca87c7c367833bf7336a2e1d2f61f2ade0d..c66fda4a65e421f347e5114ef57fe094515fc3df 100644 (file)
@@ -107,8 +107,7 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr,
                buf->vaddr = (__force void *)
                        ioremap(__pfn_to_phys(nums[0]), size + offset);
        } else {
-               buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1,
-                                       PAGE_KERNEL);
+               buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1);
        }
 
        if (!buf->vaddr)
index 4175d06ffd47b0864317c4dd71e121e83d9b47b1..1ef5af9a8c8bcde929989535dc290e66d599c102 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright 2018 Qtechnology A/S
  *
- * Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ * Ricardo Ribalda <ribalda@kernel.org>
  */
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -1120,5 +1120,5 @@ static struct i2c_driver imx214_i2c_driver = {
 module_i2c_driver(imx214_i2c_driver);
 
 MODULE_DESCRIPTION("Sony IMX214 Camera driver");
-MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
+MODULE_AUTHOR("Ricardo Ribalda <ribalda@kernel.org>");
 MODULE_LICENSE("GPL v2");
index def5e93849d2f520a3f89e627fd9174c293064a9..065b572e0272f4729b0aba1604fe040014a8f0db 100644 (file)
@@ -58,15 +58,15 @@ static const struct sdio_device_id smssdio_ids[] = {
         .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
        {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
         .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_MING),
        .driver_data = SMS1XXX_BOARD_SIANO_MING},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_PELE),
        .driver_data = SMS1XXX_BOARD_SIANO_PELE},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_RIO),
        .driver_data = SMS1XXX_BOARD_SIANO_RIO},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_2160),
        .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_1530),
        .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
        { /* end: all zeroes */ },
 };
index 5f8883031c9cf9dd723bdfbcc138041b1f1b55b9..0d8372cc364a7461b5d4b99b48602b681056765c 100644 (file)
@@ -92,7 +92,7 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
 {
        struct ivtv_dma_page_info user_dma;
        struct ivtv_user_dma *dma = &itv->udma;
-       int i, err;
+       int err;
 
        IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr);
 
@@ -111,16 +111,15 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
                return -EINVAL;
        }
 
-       /* Get user pages for DMA Xfer */
-       err = get_user_pages_unlocked(user_dma.uaddr, user_dma.page_count,
+       /* Pin user pages for DMA Xfer */
+       err = pin_user_pages_unlocked(user_dma.uaddr, user_dma.page_count,
                        dma->map, FOLL_FORCE);
 
        if (user_dma.page_count != err) {
                IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n",
                           err, user_dma.page_count);
                if (err >= 0) {
-                       for (i = 0; i < err; i++)
-                               put_page(dma->map[i]);
+                       unpin_user_pages(dma->map, err);
                        return -EINVAL;
                }
                return err;
@@ -130,9 +129,7 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
 
        /* Fill SG List with new values */
        if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) {
-               for (i = 0; i < dma->page_count; i++) {
-                       put_page(dma->map[i]);
-               }
+               unpin_user_pages(dma->map, dma->page_count);
                dma->page_count = 0;
                return -ENOMEM;
        }
@@ -153,7 +150,6 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
 void ivtv_udma_unmap(struct ivtv *itv)
 {
        struct ivtv_user_dma *dma = &itv->udma;
-       int i;
 
        IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n");
 
@@ -169,10 +165,7 @@ void ivtv_udma_unmap(struct ivtv *itv)
        /* sync DMA */
        ivtv_udma_sync_for_cpu(itv);
 
-       /* Release User Pages */
-       for (i = 0; i < dma->page_count; i++) {
-               put_page(dma->map[i]);
-       }
+       unpin_user_pages(dma->map, dma->page_count);
        dma->page_count = 0;
 }
 
index cd2fe2d444c0a8e2515c6fb55987213380e5d861..5f7dc9771f8d64585fb0ec0042b28f05f51c6314 100644 (file)
@@ -30,7 +30,6 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
        struct yuv_playback_info *yi = &itv->yuv_info;
        u8 frame = yi->draw_frame;
        struct yuv_frame_info *f = &yi->new_frame_info[frame];
-       int i;
        int y_pages, uv_pages;
        unsigned long y_buffer_offset, uv_buffer_offset;
        int y_decode_height, uv_decode_height, y_size;
@@ -62,12 +61,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
        ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height);
        ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height);
 
-       /* Get user pages for DMA Xfer */
-       y_pages = get_user_pages_unlocked(y_dma.uaddr,
+       /* Pin user pages for DMA Xfer */
+       y_pages = pin_user_pages_unlocked(y_dma.uaddr,
                        y_dma.page_count, &dma->map[0], FOLL_FORCE);
        uv_pages = 0; /* silence gcc. value is set and consumed only if: */
        if (y_pages == y_dma.page_count) {
-               uv_pages = get_user_pages_unlocked(uv_dma.uaddr,
+               uv_pages = pin_user_pages_unlocked(uv_dma.uaddr,
                                uv_dma.page_count, &dma->map[y_pages],
                                FOLL_FORCE);
        }
@@ -81,8 +80,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
                                 uv_pages, uv_dma.page_count);
 
                        if (uv_pages >= 0) {
-                               for (i = 0; i < uv_pages; i++)
-                                       put_page(dma->map[y_pages + i]);
+                               unpin_user_pages(&dma->map[y_pages], uv_pages);
                                rc = -EFAULT;
                        } else {
                                rc = uv_pages;
@@ -93,8 +91,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
                                 y_pages, y_dma.page_count);
                }
                if (y_pages >= 0) {
-                       for (i = 0; i < y_pages; i++)
-                               put_page(dma->map[i]);
+                       unpin_user_pages(dma->map, y_pages);
                        /*
                         * Inherit the -EFAULT from rc's
                         * initialization, but allow it to be
@@ -112,9 +109,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
        /* Fill & map SG List */
        if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) {
                IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n");
-               for (i = 0; i < dma->page_count; i++) {
-                       put_page(dma->map[i]);
-               }
+               unpin_user_pages(dma->map, dma->page_count);
                dma->page_count = 0;
                return -ENOMEM;
        }
index 0c285984408162a6380e82db46e96ea75ed2890b..e2d56dca5be40986d68e64be4305beeadeaef811 100644 (file)
@@ -281,10 +281,10 @@ static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv,
        /* Map User DMA */
        if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) {
                mutex_unlock(&itv->udma.lock);
-               IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with get_user_pages: %d bytes, %d pages returned\n",
+               IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with pin_user_pages: %d bytes, %d pages returned\n",
                               size_in_bytes, itv->udma.page_count);
 
-               /* get_user_pages must have failed completely */
+               /* pin_user_pages must have failed completely */
                return -EIO;
        }
 
index 0655aa9ecf283a6a73231d12413300a4f7a4c3fa..beae6aa12638a95bcf635a941c4cda7a7c9987c7 100644 (file)
@@ -661,7 +661,6 @@ static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio,
        u8 *data = pulse8->data + 1;
        u8 cmd[2];
        int err;
-       struct tm tm;
        time64_t date;
 
        pulse8->vers = 0;
@@ -682,10 +681,7 @@ static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio,
        if (err)
                return err;
        date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
-       time64_to_tm(date, 0, &tm);
-       dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n",
-                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
-                tm.tm_hour, tm.tm_min, tm.tm_sec);
+       dev_info(pulse8->dev, "Firmware build date %ptT\n", &date);
 
        dev_dbg(pulse8->dev, "Persistent config:\n");
        cmd[0] = MSGCODE_GET_AUTO_ENABLED;
index 97f0f8b23b5ddb8221bf208817afc126f76aee3a..8a1e1b95b3793b72cadce2478ccb0f81c2f9e336 100644 (file)
@@ -980,7 +980,7 @@ static int v4l2_fwnode_reference_parse(struct device *dev,
  *
  * THIS EXAMPLE EXISTS MERELY TO DOCUMENT THIS FUNCTION. DO NOT USE IT AS A
  * REFERENCE IN HOW ACPI TABLES SHOULD BE WRITTEN!! See documentation under
- * Documentation/acpi/dsd instead and especially graph.txt,
+ * Documentation/firmware-guide/acpi/dsd/ instead and especially graph.txt,
  * data-node-references.txt and leds.txt .
  *
  *     Scope (\_SB.PCI0.I2C2)
index 0a59249198d34fa5cd87f00a32a333e0b938318f..687e9c848053b7c7fd6827e38d46fc511cfef7c0 100644 (file)
@@ -407,6 +407,21 @@ config MFD_EXYNOS_LPASS
          Select this option to enable support for Samsung Exynos Low Power
          Audio Subsystem.
 
+config MFD_GATEWORKS_GSC
+       tristate "Gateworks System Controller"
+       depends on (I2C && OF)
+       select MFD_CORE
+       select REGMAP_I2C
+       select REGMAP_IRQ
+       help
+         Enable support for the Gateworks System Controller (GSC) found
+         on Gateworks Single Board Computers supporting system functions
+         such as push-button monitor, multiple ADC's for voltage and
+         temperature monitoring, fan controller and watchdog monitor.
+         This driver provides common support for accessing the device.
+         Additional drivers must be enabled in order to use the
+         functionality of the device.
+
 config MFD_MC13XXX
        tristate
        depends on (SPI_MASTER || I2C)
@@ -551,7 +566,7 @@ config INTEL_SOC_PMIC
 
 config INTEL_SOC_PMIC_BXTWC
        tristate "Support for Intel Broxton Whiskey Cove PMIC"
-       depends on INTEL_PMC_IPC
+       depends on MFD_INTEL_PMC_BXT
        select MFD_CORE
        select REGMAP_IRQ
        help
@@ -593,7 +608,7 @@ config INTEL_SOC_PMIC_MRFLD
        tristate "Support for Intel Merrifield Basin Cove PMIC"
        depends on GPIOLIB
        depends on ACPI
-       depends on INTEL_SCU_IPC
+       depends on INTEL_SCU
        select MFD_CORE
        select REGMAP_IRQ
        help
@@ -625,13 +640,27 @@ config MFD_INTEL_LPSS_PCI
 
 config MFD_INTEL_MSIC
        bool "Intel MSIC"
-       depends on INTEL_SCU_IPC
+       depends on INTEL_SCU
        select MFD_CORE
        help
          Select this option to enable access to Intel MSIC (Avatele
          Passage) chip. This chip embeds audio, battery, GPIO, etc.
          devices used in Intel Medfield platforms.
 
+config MFD_INTEL_PMC_BXT
+       tristate "Intel PMC Driver for Broxton"
+       depends on X86
+       depends on X86_PLATFORM_DEVICES
+       depends on ACPI
+       select INTEL_SCU_IPC
+       select MFD_CORE
+       help
+         This driver provides support for the PMC (Power Management
+         Controller) on Intel Broxton and Apollo Lake. The PMC is a
+         multi-function device that exposes IPC, General Control
+         Register and P-unit access. In addition this creates devices
+         for iTCO watchdog and telemetry that are part of the PMC.
+
 config MFD_IPAQ_MICRO
        bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
        depends on SA1100_H3100 || SA1100_H3600
index f935d10cbf0fccccc1033de2e15a0c074410d618..bea2be4198226874dd0c4908de262f2cc38a3764 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_BCM590XX)    += bcm590xx.o
 obj-$(CONFIG_MFD_BD9571MWV)    += bd9571mwv.o
 obj-$(CONFIG_MFD_CROS_EC_DEV)  += cros_ec_dev.o
 obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
+obj-$(CONFIG_MFD_GATEWORKS_GSC)        += gateworks-gsc.o
 
 obj-$(CONFIG_HTC_PASIC3)       += htc-pasic3.o
 obj-$(CONFIG_HTC_I2CPLD)       += htc-i2cpld.o
@@ -212,6 +213,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS)        += intel-lpss.o
 obj-$(CONFIG_MFD_INTEL_LPSS_PCI)       += intel-lpss-pci.o
 obj-$(CONFIG_MFD_INTEL_LPSS_ACPI)      += intel-lpss-acpi.o
 obj-$(CONFIG_MFD_INTEL_MSIC)   += intel_msic.o
+obj-$(CONFIG_MFD_INTEL_PMC_BXT)        += intel_pmc_bxt.o
 obj-$(CONFIG_MFD_PALMAS)       += palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
 obj-$(CONFIG_MFD_RC5T583)      += rc5t583.o rc5t583-irq.o
diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c
new file mode 100644 (file)
index 0000000..576da62
--- /dev/null
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The Gateworks System Controller (GSC) is a multi-function
+ * device designed for use in Gateworks Single Board Computers.
+ * The control interface is I2C, with an interrupt. The device supports
+ * system functions such as push-button monitoring, multiple ADC's for
+ * voltage and temperature monitoring, fan controller and watchdog monitor.
+ *
+ * Copyright (C) 2020 Gateworks Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/gsc.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+
+/*
+ * The GSC suffers from an errata where occasionally during
+ * ADC cycles the chip can NAK I2C transactions. To ensure we have reliable
+ * register access we place retries around register access.
+ */
+#define I2C_RETRIES    3
+
+int gsc_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct i2c_client *client = context;
+       int retry, ret;
+
+       for (retry = 0; retry < I2C_RETRIES; retry++) {
+               ret = i2c_smbus_write_byte_data(client, reg, val);
+               /*
+                * -EAGAIN returned when the i2c host controller is busy
+                * -EIO returned when i2c device is busy
+                */
+               if (ret != -EAGAIN && ret != -EIO)
+                       break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gsc_write);
+
+int gsc_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct i2c_client *client = context;
+       int retry, ret;
+
+       for (retry = 0; retry < I2C_RETRIES; retry++) {
+               ret = i2c_smbus_read_byte_data(client, reg);
+               /*
+                * -EAGAIN returned when the i2c host controller is busy
+                * -EIO returned when i2c device is busy
+                */
+               if (ret != -EAGAIN && ret != -EIO)
+                       break;
+       }
+       *val = ret & 0xff;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gsc_read);
+
+/*
+ * gsc_powerdown - API to use GSC to power down board for a specific time
+ *
+ * secs - number of seconds to remain powered off
+ */
+static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs)
+{
+       int ret;
+       unsigned char regs[4];
+
+       dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n",
+                secs);
+
+       put_unaligned_le32(secs, regs);
+       ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4);
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
+                                BIT(GSC_CTRL_1_SLEEP_ADD),
+                                BIT(GSC_CTRL_1_SLEEP_ADD));
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1,
+                                BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
+                                BIT(GSC_CTRL_1_SLEEP_ENABLE),
+                                BIT(GSC_CTRL_1_SLEEP_ACTIVATE) |
+                                BIT(GSC_CTRL_1_SLEEP_ENABLE));
+
+
+       return ret;
+}
+
+static ssize_t gsc_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct gsc_dev *gsc = dev_get_drvdata(dev);
+       const char *name = attr->attr.name;
+       int rz = 0;
+
+       if (strcasecmp(name, "fw_version") == 0)
+               rz = sprintf(buf, "%d\n", gsc->fwver);
+       else if (strcasecmp(name, "fw_crc") == 0)
+               rz = sprintf(buf, "0x%04x\n", gsc->fwcrc);
+       else
+               dev_err(dev, "invalid command: '%s'\n", name);
+
+       return rz;
+}
+
+static ssize_t gsc_store(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct gsc_dev *gsc = dev_get_drvdata(dev);
+       const char *name = attr->attr.name;
+       long value;
+
+       if (strcasecmp(name, "powerdown") == 0) {
+               if (kstrtol(buf, 0, &value) == 0)
+                       gsc_powerdown(gsc, value);
+       } else {
+               dev_err(dev, "invalid command: '%s\n", name);
+       }
+
+       return count;
+}
+
+static struct device_attribute attr_fwver =
+       __ATTR(fw_version, 0440, gsc_show, NULL);
+static struct device_attribute attr_fwcrc =
+       __ATTR(fw_crc, 0440, gsc_show, NULL);
+static struct device_attribute attr_pwrdown =
+       __ATTR(powerdown, 0220, NULL, gsc_store);
+
+static struct attribute *gsc_attrs[] = {
+       &attr_fwver.attr,
+       &attr_fwcrc.attr,
+       &attr_pwrdown.attr,
+       NULL,
+};
+
+static struct attribute_group attr_group = {
+       .attrs = gsc_attrs,
+};
+
+static const struct of_device_id gsc_of_match[] = {
+       { .compatible = "gw,gsc", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, gsc_of_match);
+
+static struct regmap_bus gsc_regmap_bus = {
+       .reg_read = gsc_read,
+       .reg_write = gsc_write,
+};
+
+static const struct regmap_config gsc_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .cache_type = REGCACHE_NONE,
+       .max_register = GSC_WP,
+};
+
+static const struct regmap_irq gsc_irqs[] = {
+       REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)),
+       REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)),
+       REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)),
+       REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)),
+       REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)),
+       REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)),
+       REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)),
+       REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)),
+};
+
+static const struct regmap_irq_chip gsc_irq_chip = {
+       .name = "gateworks-gsc",
+       .irqs = gsc_irqs,
+       .num_irqs = ARRAY_SIZE(gsc_irqs),
+       .num_regs = 1,
+       .status_base = GSC_IRQ_STATUS,
+       .mask_base = GSC_IRQ_ENABLE,
+       .mask_invert = true,
+       .ack_base = GSC_IRQ_STATUS,
+       .ack_invert = true,
+};
+
+static int gsc_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct gsc_dev *gsc;
+       struct regmap_irq_chip_data *irq_data;
+       int ret;
+       unsigned int reg;
+
+       gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL);
+       if (!gsc)
+               return -ENOMEM;
+
+       gsc->dev = &client->dev;
+       gsc->i2c = client;
+       i2c_set_clientdata(client, gsc);
+
+       gsc->regmap = devm_regmap_init(dev, &gsc_regmap_bus, client,
+                                      &gsc_regmap_config);
+       if (IS_ERR(gsc->regmap))
+               return PTR_ERR(gsc->regmap);
+
+       if (regmap_read(gsc->regmap, GSC_FW_VER, &reg))
+               return -EIO;
+       gsc->fwver = reg;
+
+       regmap_read(gsc->regmap, GSC_FW_CRC, &reg);
+       gsc->fwcrc = reg;
+       regmap_read(gsc->regmap, GSC_FW_CRC + 1, &reg);
+       gsc->fwcrc |= reg << 8;
+
+       gsc->i2c_hwmon = devm_i2c_new_dummy_device(dev, client->adapter,
+                                                  GSC_HWMON);
+       if (IS_ERR(gsc->i2c_hwmon)) {
+               dev_err(dev, "Failed to allocate I2C device for HWMON\n");
+               return PTR_ERR(gsc->i2c_hwmon);
+       }
+
+       ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq,
+                                      IRQF_ONESHOT | IRQF_SHARED |
+                                      IRQF_TRIGGER_FALLING, 0,
+                                      &gsc_irq_chip, &irq_data);
+       if (ret)
+               return ret;
+
+       dev_info(dev, "Gateworks System Controller v%d: fw 0x%04x\n",
+                gsc->fwver, gsc->fwcrc);
+
+       ret = sysfs_create_group(&dev->kobj, &attr_group);
+       if (ret)
+               dev_err(dev, "failed to create sysfs attrs\n");
+
+       ret = devm_of_platform_populate(dev);
+       if (ret) {
+               sysfs_remove_group(&dev->kobj, &attr_group);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int gsc_remove(struct i2c_client *client)
+{
+       sysfs_remove_group(&client->dev.kobj, &attr_group);
+
+       return 0;
+}
+
+static struct i2c_driver gsc_driver = {
+       .driver = {
+               .name   = "gateworks-gsc",
+               .of_match_table = gsc_of_match,
+       },
+       .probe_new      = gsc_probe,
+       .remove         = gsc_remove,
+};
+module_i2c_driver(gsc_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("I2C Core interface for GSC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/intel_pmc_bxt.c b/drivers/mfd/intel_pmc_bxt.c
new file mode 100644 (file)
index 0000000..9f01d38
--- /dev/null
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the Intel Broxton PMC
+ *
+ * (C) Copyright 2014 - 2020 Intel Corporation
+ *
+ * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
+ * Sreedhara DS <sreedhara.ds@intel.com>
+ *
+ * The PMC (Power Management Controller) running on the ARC processor
+ * communicates with another entity running in the IA (Intel Architecture)
+ * core through an IPC (Intel Processor Communications) mechanism which in
+ * turn sends messages between the IA and the PMC.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_pmc_bxt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/itco_wdt.h>
+
+#include <asm/intel_scu_ipc.h>
+
+/* Residency with clock rate at 19.2MHz to usecs */
+#define S0IX_RESIDENCY_IN_USECS(d, s)          \
+({                                             \
+       u64 result = 10ull * ((d) + (s));       \
+       do_div(result, 192);                    \
+       result;                                 \
+})
+
+/* Resources exported from IFWI */
+#define PLAT_RESOURCE_IPC_INDEX                0
+#define PLAT_RESOURCE_IPC_SIZE         0x1000
+#define PLAT_RESOURCE_GCR_OFFSET       0x1000
+#define PLAT_RESOURCE_GCR_SIZE         0x1000
+#define PLAT_RESOURCE_BIOS_DATA_INDEX  1
+#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
+#define PLAT_RESOURCE_TELEM_SSRAM_INDEX        3
+#define PLAT_RESOURCE_ISP_DATA_INDEX   4
+#define PLAT_RESOURCE_ISP_IFACE_INDEX  5
+#define PLAT_RESOURCE_GTD_DATA_INDEX   6
+#define PLAT_RESOURCE_GTD_IFACE_INDEX  7
+#define PLAT_RESOURCE_ACPI_IO_INDEX    0
+
+/*
+ * BIOS does not create an ACPI device for each PMC function, but
+ * exports multiple resources from one ACPI device (IPC) for multiple
+ * functions. This driver is responsible for creating a child device and
+ * to export resources for those functions.
+ */
+#define SMI_EN_OFFSET                  0x0040
+#define SMI_EN_SIZE                    4
+#define TCO_BASE_OFFSET                        0x0060
+#define TCO_REGS_SIZE                  16
+#define TELEM_SSRAM_SIZE               240
+#define TELEM_PMC_SSRAM_OFFSET         0x1b00
+#define TELEM_PUNIT_SSRAM_OFFSET       0x1a00
+
+/* Commands */
+#define PMC_NORTHPEAK_CTRL             0xed
+
+static inline bool is_gcr_valid(u32 offset)
+{
+       return offset < PLAT_RESOURCE_GCR_SIZE - 8;
+}
+
+/**
+ * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
+ * @pmc: PMC device pointer
+ * @offset: offset of GCR register from GCR address base
+ * @data: data pointer for storing the register output
+ *
+ * Reads the 64-bit PMC GCR register at given offset.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
+{
+       if (!is_gcr_valid(offset))
+               return -EINVAL;
+
+       spin_lock(&pmc->gcr_lock);
+       *data = readq(pmc->gcr_mem_base + offset);
+       spin_unlock(&pmc->gcr_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
+
+/**
+ * intel_pmc_gcr_update() - Update PMC GCR register bits
+ * @pmc: PMC device pointer
+ * @offset: offset of GCR register from GCR address base
+ * @mask: bit mask for update operation
+ * @val: update value
+ *
+ * Updates the bits of given GCR register as specified by
+ * @mask and @val.
+ *
+ * Return: Negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val)
+{
+       u32 new_val;
+
+       if (!is_gcr_valid(offset))
+               return -EINVAL;
+
+       spin_lock(&pmc->gcr_lock);
+       new_val = readl(pmc->gcr_mem_base + offset);
+
+       new_val = (new_val & ~mask) | (val & mask);
+       writel(new_val, pmc->gcr_mem_base + offset);
+
+       new_val = readl(pmc->gcr_mem_base + offset);
+       spin_unlock(&pmc->gcr_lock);
+
+       /* Check whether the bit update is successful */
+       return (new_val & mask) != (val & mask) ? -EIO : 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
+
+/**
+ * intel_pmc_s0ix_counter_read() - Read S0ix residency
+ * @pmc: PMC device pointer
+ * @data: Out param that contains current S0ix residency count.
+ *
+ * Writes to @data how many usecs the system has been in low-power S0ix
+ * state.
+ *
+ * Return: An error code or 0 on success.
+ */
+int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
+{
+       u64 deep, shlw;
+
+       spin_lock(&pmc->gcr_lock);
+       deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
+       shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
+       spin_unlock(&pmc->gcr_lock);
+
+       *data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
+
+/**
+ * simplecmd_store() - Send a simple IPC command
+ * @dev: Device under the attribute is
+ * @attr: Attribute in question
+ * @buf: Buffer holding data to be stored to the attribute
+ * @count: Number of bytes in @buf
+ *
+ * Expects a string with two integers separated with space. These two
+ * values hold command and subcommand that is send to PMC.
+ *
+ * Return: Number number of bytes written (@count) or negative errno in
+ *        case of error.
+ */
+static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
+       struct intel_scu_ipc_dev *scu = pmc->scu;
+       int subcmd;
+       int cmd;
+       int ret;
+
+       ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+       if (ret != 2) {
+               dev_err(dev, "Invalid values, expected: cmd subcmd\n");
+               return -EINVAL;
+       }
+
+       ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
+       if (ret)
+               return ret;
+
+       return count;
+}
+static DEVICE_ATTR_WO(simplecmd);
+
+/**
+ * northpeak_store() - Enable or disable Northpeak
+ * @dev: Device under the attribute is
+ * @attr: Attribute in question
+ * @buf: Buffer holding data to be stored to the attribute
+ * @count: Number of bytes in @buf
+ *
+ * Expects an unsigned integer. Non-zero enables Northpeak and zero
+ * disables it.
+ *
+ * Return: Number number of bytes written (@count) or negative errno in
+ *        case of error.
+ */
+static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
+       struct intel_scu_ipc_dev *scu = pmc->scu;
+       unsigned long val;
+       int subcmd;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       /* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */
+       if (val)
+               subcmd = 1;
+       else
+               subcmd = 0;
+
+       ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
+       if (ret)
+               return ret;
+
+       return count;
+}
+static DEVICE_ATTR_WO(northpeak);
+
+static struct attribute *intel_pmc_attrs[] = {
+       &dev_attr_northpeak.attr,
+       &dev_attr_simplecmd.attr,
+       NULL
+};
+
+static const struct attribute_group intel_pmc_group = {
+       .attrs = intel_pmc_attrs,
+};
+
+static const struct attribute_group *intel_pmc_groups[] = {
+       &intel_pmc_group,
+       NULL
+};
+
+static struct resource punit_res[6];
+
+static struct mfd_cell punit = {
+       .name = "intel_punit_ipc",
+       .resources = punit_res,
+};
+
+static struct itco_wdt_platform_data tco_pdata = {
+       .name = "Apollo Lake SoC",
+       .version = 5,
+       .no_reboot_use_pmc = true,
+};
+
+static struct resource tco_res[2];
+
+static const struct mfd_cell tco = {
+       .name = "iTCO_wdt",
+       .ignore_resource_conflicts = true,
+       .resources = tco_res,
+       .num_resources = ARRAY_SIZE(tco_res),
+       .platform_data = &tco_pdata,
+       .pdata_size = sizeof(tco_pdata),
+};
+
+static const struct resource telem_res[] = {
+       DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
+       DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
+};
+
+static const struct mfd_cell telem = {
+       .name = "intel_telemetry",
+       .resources = telem_res,
+       .num_resources = ARRAY_SIZE(telem_res),
+};
+
+static int intel_pmc_get_tco_resources(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       if (acpi_has_watchdog())
+               return 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_IO,
+                                   PLAT_RESOURCE_ACPI_IO_INDEX);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get IO resource\n");
+               return -EINVAL;
+       }
+
+       tco_res[0].flags = IORESOURCE_IO;
+       tco_res[0].start = res->start + TCO_BASE_OFFSET;
+       tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
+       tco_res[1].flags = IORESOURCE_IO;
+       tco_res[1].start = res->start + SMI_EN_OFFSET;
+       tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
+
+       return 0;
+}
+
+static int intel_pmc_get_resources(struct platform_device *pdev,
+                                  struct intel_pmc_dev *pmc,
+                                  struct intel_scu_ipc_data *scu_data)
+{
+       struct resource gcr_res;
+       size_t npunit_res = 0;
+       struct resource *res;
+       int ret;
+
+       scu_data->irq = platform_get_irq_optional(pdev, 0);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_IPC_INDEX);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get IPC resource\n");
+               return -EINVAL;
+       }
+
+       /* IPC registers */
+       scu_data->mem.flags = res->flags;
+       scu_data->mem.start = res->start;
+       scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
+
+       /* GCR registers */
+       gcr_res.flags = res->flags;
+       gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
+       gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
+
+       pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
+       if (IS_ERR(pmc->gcr_mem_base))
+               return PTR_ERR(pmc->gcr_mem_base);
+
+       /* Only register iTCO watchdog if there is no WDAT ACPI table */
+       ret = intel_pmc_get_tco_resources(pdev);
+       if (ret)
+               return ret;
+
+       /* BIOS data register */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_BIOS_DATA_INDEX);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n");
+               return -EINVAL;
+       }
+       punit_res[npunit_res++] = *res;
+
+       /* BIOS interface register */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_BIOS_IFACE_INDEX);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
+               return -EINVAL;
+       }
+       punit_res[npunit_res++] = *res;
+
+       /* ISP data register, optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_ISP_DATA_INDEX);
+       if (res)
+               punit_res[npunit_res++] = *res;
+
+       /* ISP interface register, optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_ISP_IFACE_INDEX);
+       if (res)
+               punit_res[npunit_res++] = *res;
+
+       /* GTD data register, optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_GTD_DATA_INDEX);
+       if (res)
+               punit_res[npunit_res++] = *res;
+
+       /* GTD interface register, optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_GTD_IFACE_INDEX);
+       if (res)
+               punit_res[npunit_res++] = *res;
+
+       punit.num_resources = npunit_res;
+
+       /* Telemetry SSRAM is optional */
+       res = platform_get_resource(pdev, IORESOURCE_MEM,
+                                   PLAT_RESOURCE_TELEM_SSRAM_INDEX);
+       if (res)
+               pmc->telem_base = res;
+
+       return 0;
+}
+
+static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
+{
+       int ret;
+
+       if (!acpi_has_watchdog()) {
+               ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco,
+                                          1, NULL, 0, NULL);
+               if (ret)
+                       return ret;
+       }
+
+       ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1,
+                                  NULL, 0, NULL);
+       if (ret)
+               return ret;
+
+       if (pmc->telem_base) {
+               ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
+                                          &telem, 1, pmc->telem_base, 0, NULL);
+       }
+
+       return ret;
+}
+
+static const struct acpi_device_id intel_pmc_acpi_ids[] = {
+       { "INT34D2" },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
+
+static int intel_pmc_probe(struct platform_device *pdev)
+{
+       struct intel_scu_ipc_data scu_data = {};
+       struct intel_pmc_dev *pmc;
+       int ret;
+
+       pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+       if (!pmc)
+               return -ENOMEM;
+
+       pmc->dev = &pdev->dev;
+       spin_lock_init(&pmc->gcr_lock);
+
+       ret = intel_pmc_get_resources(pdev, pmc, &scu_data);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request resources\n");
+               return ret;
+       }
+
+       pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
+       if (IS_ERR(pmc->scu))
+               return PTR_ERR(pmc->scu);
+
+       platform_set_drvdata(pdev, pmc);
+
+       ret = intel_pmc_create_devices(pmc);
+       if (ret)
+               dev_err(&pdev->dev, "Failed to create PMC devices\n");
+
+       return ret;
+}
+
+static struct platform_driver intel_pmc_driver = {
+       .probe = intel_pmc_probe,
+       .driver = {
+               .name = "intel_pmc_bxt",
+               .acpi_match_table = intel_pmc_acpi_ids,
+               .dev_groups = intel_pmc_groups,
+       },
+};
+module_platform_driver(intel_pmc_driver);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
+MODULE_DESCRIPTION("Intel Broxton PMC driver");
+MODULE_LICENSE("GPL v2");
index 739cfb5b69fe75570c2e2e4ead95b26220f3fabb..eba89780dbe75de0c7b6fa19379f68952104e544 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/mfd/intel_soc_pmic_bxtwc.h>
 #include <linux/module.h>
 
-#include <asm/intel_pmc_ipc.h>
+#include <asm/intel_scu_ipc.h>
 
 /* PMIC device registers */
 #define REG_ADDR_MASK          0xFF00
 /* Whiskey Cove PMIC share same ACPI ID between different platforms */
 #define BROXTON_PMIC_WC_HRV    4
 
+#define PMC_PMIC_ACCESS                0xFF
+#define PMC_PMIC_READ          0x0
+#define PMC_PMIC_WRITE         0x1
+
 enum bxtwc_irqs {
        BXTWC_PWRBTN_LVL1_IRQ = 0,
        BXTWC_TMU_LVL1_IRQ,
@@ -288,13 +292,12 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 
        ipc_in[0] = reg;
        ipc_in[1] = i2c_addr;
-       ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-                       PMC_IPC_PMIC_ACCESS_READ,
-                       ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
-       if (ret) {
-               dev_err(pmic->dev, "Failed to read from PMIC\n");
+       ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
+                                       PMC_PMIC_READ, ipc_in, sizeof(ipc_in),
+                                       ipc_out, sizeof(ipc_out));
+       if (ret)
                return ret;
-       }
+
        *val = ipc_out[0];
 
        return 0;
@@ -303,7 +306,6 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
 static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
                                       unsigned int val)
 {
-       int ret;
        int i2c_addr;
        u8 ipc_in[3];
        struct intel_soc_pmic *pmic = context;
@@ -321,15 +323,9 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
        ipc_in[0] = reg;
        ipc_in[1] = i2c_addr;
        ipc_in[2] = val;
-       ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
-                       PMC_IPC_PMIC_ACCESS_WRITE,
-                       ipc_in, sizeof(ipc_in), NULL, 0);
-       if (ret) {
-               dev_err(pmic->dev, "Failed to write to PMIC\n");
-               return ret;
-       }
-
-       return 0;
+       return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
+                                        PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in),
+                                        NULL, 0);
 }
 
 /* sysfs interfaces to r/w PMIC registers, required by initial script */
@@ -457,6 +453,10 @@ static int bxtwc_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, pmic);
        pmic->dev = &pdev->dev;
 
+       pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+       if (!pmic->scu)
+               return -EPROBE_DEFER;
+
        pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
                                        &bxtwc_regmap_config);
        if (IS_ERR(pmic->regmap)) {
index 26a1551c5faf3233bf49cf458595380acea67493..bd94c989d232e17662cae841def556a4ba385760 100644 (file)
@@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = {
 static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
                                    unsigned int *val)
 {
+       struct intel_soc_pmic *pmic = context;
        u8 ipc_out;
        int ret;
 
-       ret = intel_scu_ipc_ioread8(reg, &ipc_out);
+       ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out);
        if (ret)
                return ret;
 
@@ -88,10 +89,11 @@ static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
 static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
                                     unsigned int val)
 {
+       struct intel_soc_pmic *pmic = context;
        u8 ipc_in = val;
        int ret;
 
-       ret = intel_scu_ipc_iowrite8(reg, ipc_in);
+       ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
        if (ret)
                return ret;
 
@@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev)
        if (!pmic)
                return -ENOMEM;
 
+       pmic->scu = devm_intel_scu_ipc_dev_get(dev);
+       if (!pmic->scu)
+               return -ENOMEM;
+
        platform_set_drvdata(pdev, pmic);
        pmic->dev = &pdev->dev;
 
index a1ed375fed37443084d5eabbcd74faef157ec52d..71f795b510ced321d4d56abb5a63d0cea4a25d20 100644 (file)
@@ -241,7 +241,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * MEI requires to resume from runtime suspend mode
         * in order to perform link reset flow upon system suspend.
         */
-       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        /*
         * ME maps runtime suspend/resume to D0i states,
index beacf2a2f2b57c68f9ad1b7088b06efee527974b..4bf26ce61044b83fb3433b7a0fe1fdc5408d564d 100644 (file)
@@ -128,7 +128,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
         * MEI requires to resume from runtime suspend mode
         * in order to perform link reset flow upon system suspend.
         */
-       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        /*
         * TXE maps runtime suspend/resume to own power gating states,
index ce16d6b99295e088b670183496527e81a20b5d64..2d8328d928d53fe78832be7ea6cb793a123b1931 100644 (file)
@@ -233,8 +233,6 @@ static int vmci_host_setup_notify(struct vmci_ctx *context,
         * about the size.
         */
        BUILD_BUG_ON(sizeof(bool) != sizeof(u8));
-       if (!access_ok((void __user *)uva, sizeof(u8)))
-               return VMCI_ERROR_GENERIC;
 
        /*
         * Lock physical page backing a given user VA.
index c5367e2c848756ad66b2d4b5973cb760e1a6e57b..7896952de1ac75a5bc384eb065868732b64c02e3 100644 (file)
@@ -2484,8 +2484,8 @@ static int mmc_rpmb_chrdev_release(struct inode *inode, struct file *filp)
        struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
                                                  struct mmc_rpmb_data, chrdev);
 
-       put_device(&rpmb->dev);
        mmc_blk_put(rpmb->md);
+       put_device(&rpmb->dev);
 
        return 0;
 }
index 74de3f2dda38eb93b25f9ef70135dd241d465cd8..70207f11a6548e2c6c1e0ba5d38eb7b2b3aae891 100644 (file)
@@ -93,6 +93,20 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
                        return retval;
        }
 
+       if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
+               retval = add_uevent_var(env, "SDIO_ID=%04X:%04X",
+                                       card->cis.vendor, card->cis.device);
+               if (retval)
+                       return retval;
+       }
+
+       /*
+        * SDIO (non-combo) cards are not handled by mmc_block driver and do not
+        * have accessible CID register which used by mmc_card_name() function.
+        */
+       if (card->type == MMC_TYPE_SDIO)
+               return 0;
+
        retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
        if (retval)
                return retval;
index 4c5de6d37ac70e76ed2b329560ed667f7d4211eb..8d2b808e9b58612d85eece795111d6d66bd61db0 100644 (file)
@@ -1815,8 +1815,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
        unsigned int rem, to = from + nr;
        int err;
 
-       if (!(card->host->caps & MMC_CAP_ERASE) ||
-           !(card->csd.cmdclass & CCC_ERASE))
+       if (!(card->csd.cmdclass & CCC_ERASE))
                return -EOPNOTSUPP;
 
        if (!card->erase_size)
@@ -1872,8 +1871,7 @@ EXPORT_SYMBOL(mmc_erase);
 
 int mmc_can_erase(struct mmc_card *card)
 {
-       if ((card->host->caps & MMC_CAP_ERASE) &&
-           (card->csd.cmdclass & CCC_ERASE) && card->erase_size)
+       if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
                return 1;
        return 0;
 }
index 09e0c765946917e77517ac41cb0f114152abee70..9ec84c86c46af7083f7189a48628dc379eabee82 100644 (file)
@@ -219,7 +219,7 @@ static int mmc_clock_opt_set(void *data, u64 val)
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
+DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
        "%llu\n");
 
 void mmc_add_host_debugfs(struct mmc_host *host)
@@ -232,8 +232,8 @@ void mmc_add_host_debugfs(struct mmc_host *host)
        debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
        debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
        debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
-       debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
-                           &mmc_clock_fops);
+       debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
+                                  &mmc_clock_fops);
 
 #ifdef CONFIG_FAIL_MMC_REQUEST
        if (fail_request)
index de94fbe629bddad2f7974ae2ad5909682d63ef30..4203303f946a69df89c44d09a0f5439d0f6d8dee 100644 (file)
@@ -647,6 +647,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
                                 mmc_hostname(card->host),
                                 card->ext_csd.cmdq_depth);
                }
+               card->ext_csd.enhanced_rpmb_supported =
+                                       (card->ext_csd.rel_param &
+                                        EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
        }
 out:
        return err;
@@ -786,6 +789,8 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
                card->ext_csd.enhanced_area_offset);
 MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
 MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
+MMC_DEV_ATTR(enhanced_rpmb_supported, "%#x\n",
+       card->ext_csd.enhanced_rpmb_supported);
 MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
 MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
 MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
@@ -843,6 +848,7 @@ static struct attribute *mmc_std_attrs[] = {
        &dev_attr_enhanced_area_offset.attr,
        &dev_attr_enhanced_area_size.attr,
        &dev_attr_raw_rpmb_size_mult.attr,
+       &dev_attr_enhanced_rpmb_supported.attr,
        &dev_attr_rel_sectors.attr,
        &dev_attr_ocr.attr,
        &dev_attr_rca.attr,
index 3dba15bccce251e351d09779aecd52562841d723..472fa2fdcf139d6d94c0684431a53bc4587c70a6 100644 (file)
@@ -139,7 +139,7 @@ static const struct mmc_fixup sdio_fixup_methods[] = {
        SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
                   add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
 
-       SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN,
+       SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_F0,
                   add_limit_rate_quirk, 150000000),
 
        END_FIXUP
index b6febbcf8978d26b5c67105351e860eeef74af96..96b1d15045d603efa2831cd868bdb8cee3f257df 100644 (file)
@@ -136,6 +136,8 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
                                                  int min_uV, int target_uV,
                                                  int max_uV)
 {
+       int current_uV;
+
        /*
         * Check if supported first to avoid errors since we may try several
         * signal levels during power up and don't want to show errors.
@@ -143,6 +145,14 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
        if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
                return -EINVAL;
 
+       /*
+        * The voltage is already set, no need to switch.
+        * Return 1 to indicate that no switch happened.
+        */
+       current_uV = regulator_get_voltage(regulator);
+       if (current_uV == target_uV)
+               return 1;
+
        return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
                                             max_uV);
 }
@@ -198,9 +208,10 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
                 * voltage in two steps and try to stay close to vmmc
                 * with a 0.3V tolerance at first.
                 */
-               if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
-                                               min_uV, volt, max_uV))
-                       return 0;
+               ret = mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
+                                                       min_uV, volt, max_uV);
+               if (ret >= 0)
+                       return ret;
 
                return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
                                                2700000, volt, 3600000);
index 76c7add367d5c17764580a3046b17d8ec45a5b3a..5a2210c25aa7a65082c1471ab0094db46b8daa0a 100644 (file)
@@ -376,11 +376,11 @@ int mmc_sd_switch_hs(struct mmc_card *card)
        if (!status)
                return -ENOMEM;
 
-       err = mmc_sd_switch(card, 1, 0, 1, status);
+       err = mmc_sd_switch(card, 1, 0, HIGH_SPEED_BUS_SPEED, status);
        if (err)
                goto out;
 
-       if ((status[16] & 0xF) != 1) {
+       if ((status[16] & 0xF) != HIGH_SPEED_BUS_SPEED) {
                pr_warn("%s: Problem switching card into high-speed mode!\n",
                        mmc_hostname(card->host));
                err = 0;
@@ -707,7 +707,12 @@ static ssize_t mmc_dsr_show(struct device *dev,
 
 static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
 
+MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
+MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
+
 static struct attribute *sd_std_attrs[] = {
+       &dev_attr_vendor.attr,
+       &dev_attr_device.attr,
        &dev_attr_cid.attr,
        &dev_attr_csd.attr,
        &dev_attr_scr.attr,
@@ -726,7 +731,26 @@ static struct attribute *sd_std_attrs[] = {
        &dev_attr_dsr.attr,
        NULL,
 };
-ATTRIBUTE_GROUPS(sd_std);
+
+static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
+                                int index)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct mmc_card *card = mmc_dev_to_card(dev);
+
+       /* CIS vendor and device ids are available only for Combo cards */
+       if ((attr == &dev_attr_vendor.attr || attr == &dev_attr_device.attr) &&
+           card->type != MMC_TYPE_SD_COMBO)
+               return 0;
+
+       return attr->mode;
+}
+
+static const struct attribute_group sd_std_group = {
+       .attrs = sd_std_attrs,
+       .is_visible = sd_std_is_visible,
+};
+__ATTRIBUTE_GROUPS(sd_std);
 
 struct device_type sd_type = {
        .groups = sd_std_groups,
index ebb387aa5158463a4490786e1ab1a133a48644a6..b65b26f76d719e9f31134f2d5802ed9917f80044 100644 (file)
 #include "sdio_ops.h"
 #include "sdio_cis.h"
 
+MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
+MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
+MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
+MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
+
+static struct attribute *sdio_std_attrs[] = {
+       &dev_attr_vendor.attr,
+       &dev_attr_device.attr,
+       &dev_attr_ocr.attr,
+       &dev_attr_rca.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(sdio_std);
+
+static struct device_type sdio_type = {
+       .groups = sdio_std_groups,
+};
+
 static int sdio_read_fbr(struct sdio_func *func)
 {
        int ret;
@@ -543,13 +561,33 @@ out:
        return err;
 }
 
-static void mmc_sdio_resend_if_cond(struct mmc_host *host,
-                                   struct mmc_card *card)
+static int mmc_sdio_pre_init(struct mmc_host *host, u32 ocr,
+                            struct mmc_card *card)
 {
+       if (card)
+               mmc_remove_card(card);
+
+       /*
+        * Reset the card by performing the same steps that are taken by
+        * mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
+        *
+        * sdio_reset() is technically not needed. Having just powered up the
+        * hardware, it should already be in reset state. However, some
+        * platforms (such as SD8686 on OLPC) do not instantly cut power,
+        * meaning that a reset is required when restoring power soon after
+        * powering off. It is harmless in other cases.
+        *
+        * The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
+        * is not necessary for non-removable cards. However, it is required
+        * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
+        * harmless in other situations.
+        *
+        */
+
        sdio_reset(host);
        mmc_go_idle(host);
-       mmc_send_if_cond(host, host->ocr_avail);
-       mmc_remove_card(card);
+       mmc_send_if_cond(host, ocr);
+       return mmc_send_io_op_cond(host, 0, NULL);
 }
 
 /*
@@ -584,7 +622,7 @@ try_again:
         */
        err = mmc_send_io_op_cond(host, ocr, &rocr);
        if (err)
-               goto err;
+               return err;
 
        /*
         * For SPI, enable CRC as appropriate.
@@ -592,17 +630,15 @@ try_again:
        if (mmc_host_is_spi(host)) {
                err = mmc_spi_set_crc(host, use_spi_crc);
                if (err)
-                       goto err;
+                       return err;
        }
 
        /*
         * Allocate card structure.
         */
-       card = mmc_alloc_card(host, NULL);
-       if (IS_ERR(card)) {
-               err = PTR_ERR(card);
-               goto err;
-       }
+       card = mmc_alloc_card(host, &sdio_type);
+       if (IS_ERR(card))
+               return PTR_ERR(card);
 
        if ((rocr & R4_MEMORY_PRESENT) &&
            mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
@@ -610,19 +646,15 @@ try_again:
 
                if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
                    memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
-                       mmc_remove_card(card);
-                       pr_debug("%s: Perhaps the card was replaced\n",
-                               mmc_hostname(host));
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto mismatch;
                }
        } else {
                card->type = MMC_TYPE_SDIO;
 
                if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
-                       mmc_remove_card(card);
-                       pr_debug("%s: Perhaps the card was replaced\n",
-                               mmc_hostname(host));
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto mismatch;
                }
        }
 
@@ -646,7 +678,7 @@ try_again:
        if (rocr & ocr & R4_18V_PRESENT) {
                err = mmc_set_uhs_voltage(host, ocr_card);
                if (err == -EAGAIN) {
-                       mmc_sdio_resend_if_cond(host, card);
+                       mmc_sdio_pre_init(host, ocr_card, card);
                        retries--;
                        goto try_again;
                } else if (err) {
@@ -677,7 +709,7 @@ try_again:
        if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
                err = mmc_sd_get_csd(host, card);
                if (err)
-                       return err;
+                       goto remove;
 
                mmc_decode_cid(card);
        }
@@ -704,7 +736,12 @@ try_again:
                        mmc_set_timing(card->host, MMC_TIMING_SD_HS);
                }
 
-               goto finish;
+               if (oldcard)
+                       mmc_remove_card(card);
+               else
+                       host->card = card;
+
+               return 0;
        }
 
        /*
@@ -713,14 +750,13 @@ try_again:
         */
        err = sdio_read_cccr(card, ocr);
        if (err) {
-               mmc_sdio_resend_if_cond(host, card);
+               mmc_sdio_pre_init(host, ocr_card, card);
                if (ocr & R4_18V_PRESENT) {
                        /* Retry init sequence, but without R4_18V_PRESENT. */
                        retries = 0;
                        goto try_again;
-               } else {
-                       goto remove;
                }
+               return err;
        }
 
        /*
@@ -731,16 +767,14 @@ try_again:
                goto remove;
 
        if (oldcard) {
-               int same = (card->cis.vendor == oldcard->cis.vendor &&
-                           card->cis.device == oldcard->cis.device);
-               mmc_remove_card(card);
-               if (!same) {
-                       pr_debug("%s: Perhaps the card was replaced\n",
-                               mmc_hostname(host));
-                       return -ENOENT;
+               if (card->cis.vendor == oldcard->cis.vendor &&
+                   card->cis.device == oldcard->cis.device) {
+                       mmc_remove_card(card);
+                       card = oldcard;
+               } else {
+                       err = -ENOENT;
+                       goto mismatch;
                }
-
-               card = oldcard;
        }
        card->ocr = ocr_card;
        mmc_fixup_device(card, sdio_fixup_methods);
@@ -801,16 +835,15 @@ try_again:
                err = -EINVAL;
                goto remove;
        }
-finish:
-       if (!oldcard)
-               host->card = card;
+
+       host->card = card;
        return 0;
 
+mismatch:
+       pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
 remove:
-       if (!oldcard)
+       if (oldcard != card)
                mmc_remove_card(card);
-
-err:
        return err;
 }
 
@@ -818,28 +851,7 @@ static int mmc_sdio_reinit_card(struct mmc_host *host)
 {
        int ret;
 
-       /*
-        * Reset the card by performing the same steps that are taken by
-        * mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
-        *
-        * sdio_reset() is technically not needed. Having just powered up the
-        * hardware, it should already be in reset state. However, some
-        * platforms (such as SD8686 on OLPC) do not instantly cut power,
-        * meaning that a reset is required when restoring power soon after
-        * powering off. It is harmless in other cases.
-        *
-        * The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
-        * is not necessary for non-removable cards. However, it is required
-        * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
-        * harmless in other situations.
-        *
-        */
-
-       sdio_reset(host);
-       mmc_go_idle(host);
-       mmc_send_if_cond(host, host->card->ocr);
-
-       ret = mmc_send_io_op_cond(host, 0, NULL);
+       ret = mmc_sdio_pre_init(host, host->card->ocr, NULL);
        if (ret)
                return ret;
 
index 462b5352fea7508934ec8f4c138cf472959107bc..0ce332ad986bae99dad6d2b0f1a814794ccb035a 100644 (file)
@@ -171,7 +171,7 @@ config MMC_SDHCI_OF_ASPEED
 config MMC_SDHCI_OF_AT91
        tristate "SDHCI OF support for the Atmel SDMMC controller"
        depends on MMC_SDHCI_PLTFM
-       depends on OF
+       depends on OF && HAVE_CLK
        help
          This selects the Atmel SDMMC driver
 
@@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
 
          If unsure, say N.
 
+config MMC_SDHCI_ESDHC_MCF
+       tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
+       depends on M5441x
+       depends on MMC_SDHCI_PLTFM
+       select MMC_SDHCI_IO_ACCESSORS
+       help
+         This selects the Freescale eSDHC controller support for
+         ColdFire mcf5441x devices.
+
+         If you have a controller with this interface, say Y or M here.
+
+         If unsure, say N.
+
 config MMC_SDHCI_ESDHC_IMX
        tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
        depends on ARCH_MXC
@@ -405,6 +418,20 @@ config MMC_MESON_GX
 
          If you have a controller with this interface, say Y here.
 
+config MMC_MESON_MX_SDHC
+       tristate "Amlogic Meson SDHC Host Controller support"
+       depends on (ARM && ARCH_MESON) || COMPILE_TEST
+       depends on COMMON_CLK
+       depends on OF
+       help
+         This selects support for the SDHC Host Controller on
+         Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
+         The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
+         with 1, 4, and 8 bit bus widths.
+
+         If you have a controller with this interface, say Y or M here.
+         If unsure, say N.
+
 config MMC_MESON_MX_SDIO
        tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
        depends on ARCH_MESON || COMPILE_TEST
index b929ef941208353ecd9cb2ba998d0b7eb064340f..4d5bcb0144a0ab48a3fae174acb7fc2baf865619 100644 (file)
@@ -68,6 +68,8 @@ obj-$(CONFIG_MMC_VUB300)      += vub300.o
 obj-$(CONFIG_MMC_USHC)         += ushc.o
 obj-$(CONFIG_MMC_WMT)          += wmt-sdmmc.o
 obj-$(CONFIG_MMC_MESON_GX)     += meson-gx-mmc.o
+meson-mx-sdhc-objs             := meson-mx-sdhc-clkc.o meson-mx-sdhc-mmc.o
+obj-$(CONFIG_MMC_MESON_MX_SDHC)        += meson-mx-sdhc.o
 obj-$(CONFIG_MMC_MESON_MX_SDIO)        += meson-mx-sdio.o
 obj-$(CONFIG_MMC_MOXART)       += moxart-mmc.o
 obj-$(CONFIG_MMC_SUNXI)                += sunxi-mmc.o
@@ -82,6 +84,7 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)          += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CADENCE)                += sdhci-cadence.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)                += sdhci-cns3xxx.o
+obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF)       += sdhci-esdhc-mcf.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)      += sdhci-esdhc-imx.o
 obj-$(CONFIG_MMC_SDHCI_DOVE)           += sdhci-dove.o
 obj-$(CONFIG_MMC_SDHCI_TEGRA)          += sdhci-tegra.o
index 914e17bab3bed56ad1baccb0554a0b56a130a63f..ceb4924e02d0104ba11c41c2b430722e744e5ae5 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/mutex.h>
 #include <linux/scatterlist.h>
 #include <linux/mmc/mmc.h>
-#include <linux/mmc/sdio.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
 
@@ -404,14 +403,6 @@ static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
        host->mrq = req;
        goldfish_mmc_prepare_data(host, req);
        goldfish_mmc_start_command(host, req->cmd);
-
-       /*
-        * This is to avoid accidentally being detected as an SDIO card
-        * in mmc_attach_sdio().
-        */
-       if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
-           req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR))
-               req->cmd->error = -EINVAL;
 }
 
 static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -482,6 +473,7 @@ static int goldfish_mmc_probe(struct platform_device *pdev)
        mmc->f_max = 24000000;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
        mmc->caps = MMC_CAP_4_BIT_DATA;
+       mmc->caps2 = MMC_CAP2_NO_SDIO;
 
        /* Use scatterlist DMA to reduce per-transfer costs.
         * NOTE max_seg_size assumption that small blocks aren't
index aeaaa5314924947f8eb714171cd5ff75af5b445d..5cb6926876981616bff9da8300f2fcfa82c14afd 100644 (file)
 #define        atmci_writel(port, reg, value)                  \
        __raw_writel((value), (port)->regs + reg)
 
+#define ATMCI_CMD_TIMEOUT_MS   2000
 #define AUTOSUSPEND_DELAY      50
 
 #define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
@@ -808,6 +809,9 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
 static void atmci_send_command(struct atmel_mci *host,
                struct mmc_command *cmd, u32 cmd_flags)
 {
+       unsigned int timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
+               ATMCI_CMD_TIMEOUT_MS;
+
        WARN_ON(host->cmd);
        host->cmd = cmd;
 
@@ -817,6 +821,8 @@ static void atmci_send_command(struct atmel_mci *host,
 
        atmci_writel(host, ATMCI_ARGR, cmd->arg);
        atmci_writel(host, ATMCI_CMDR, cmd_flags);
+
+       mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
 }
 
 static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
@@ -1314,8 +1320,6 @@ static void atmci_start_request(struct atmel_mci *host,
         * prepared yet.)
         */
        atmci_writel(host, ATMCI_IER, iflags);
-
-       mod_timer(&host->timer, jiffies +  msecs_to_jiffies(2000));
 }
 
 static void atmci_queue_request(struct atmel_mci *host,
@@ -1557,6 +1561,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
 
        WARN_ON(host->cmd || host->data);
 
+       del_timer(&host->timer);
+
        /*
         * Update the MMC clock rate if necessary. This may be
         * necessary if set_ios() is called when a different slot is
@@ -1583,8 +1589,6 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
                host->state = STATE_IDLE;
        }
 
-       del_timer(&host->timer);
-
        spin_unlock(&host->lock);
        mmc_request_done(prev_mmc, mrq);
        spin_lock(&host->lock);
index 8823680ca42c97514d1024cd929bc5c4e5dd1606..9bb1910268cab7b02b5c9e1a917b11909ead93f9 100644 (file)
@@ -259,7 +259,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
        au1xmmc_finish_request(host);
 }
 
-static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
+static int au1xmmc_send_command(struct au1xmmc_host *host,
                                struct mmc_command *cmd, struct mmc_data *data)
 {
        u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
@@ -302,9 +302,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
        __raw_writel(cmd->arg, HOST_CMDARG(host));
        wmb(); /* drain writebuffer */
 
-       if (wait)
-               IRQ_OFF(host, SD_CONFIG_CR);
-
        __raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
        wmb(); /* drain writebuffer */
 
@@ -312,19 +309,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
        while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO)
                /* nop */;
 
-       /* Wait for the command to come back */
-       if (wait) {
-               u32 status = __raw_readl(HOST_STATUS(host));
-
-               while (!(status & SD_STATUS_CR))
-                       status = __raw_readl(HOST_STATUS(host));
-
-               /* Clear the CR status */
-               __raw_writel(SD_STATUS_CR, HOST_STATUS(host));
-
-               IRQ_ON(host, SD_CONFIG_CR);
-       }
-
        return 0;
 }
 
@@ -711,7 +695,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
        }
 
        if (!ret)
-               ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
+               ret = au1xmmc_send_command(host, mrq->cmd, mrq->data);
 
        if (ret) {
                mrq->cmd->error = ret;
index c3d949847cbd9d6cd1ffbf4674d93532e658233a..a0767790a826f67465109f07500475125636c967 100644 (file)
@@ -1280,8 +1280,7 @@ static int bcm2835_add_host(struct bcm2835_host *host)
 
        /* host controller capabilities */
        mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
-                    MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE |
-                    MMC_CAP_CMD23;
+                    MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_CMD23;
 
        spin_lock_init(&host->lock);
        mutex_init(&host->mutex);
index 89deb451e0ac6225c9481dc21ca21373f46c2d7c..c5da3aaee334555a8c6c71ffe30251c48df1eea6 100644 (file)
@@ -1038,8 +1038,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
         * Disable bounce buffers for max_segs = 1
         */
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
-                    MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD |
-                    MMC_CAP_3_3V_DDR;
+                    MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR;
 
        if (host->use_sg)
                mmc->max_segs = 16;
index e33270e40539de137d0a6a5a08105392a02baf1f..e84ed84ea4ccbe150c9fc61a400d34af1799f10c 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/delay.h>
 #include "cb710-mmc.h"
 
+#define CB710_MMC_REQ_TIMEOUT_MS       2000
+
 static const u8 cb710_clock_divider_log2[8] = {
 /*     1, 2, 4, 8, 16, 32, 128, 512 */
        0, 1, 2, 3,  4,  5,   7,   9
@@ -707,6 +709,12 @@ static int cb710_mmc_init(struct platform_device *pdev)
        mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
        mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
        mmc->caps = MMC_CAP_4_BIT_DATA;
+       /*
+        * In cb710_wait_for_event() we use a fixed timeout of ~2s, hence let's
+        * inform the core about it. A future improvement should instead make
+        * use of the cmd->busy_timeout.
+        */
+       mmc->max_busy_timeout = CB710_MMC_REQ_TIMEOUT_MS;
 
        reader = mmc_priv(mmc);
 
index 23b6f65b37850e009bdf12411aca857e69312b1e..50977ff18074175d5832f8d66fa9499da38daa09 100644 (file)
@@ -424,7 +424,7 @@ static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc,
 
        if (!IS_ERR(mmc->supply.vqmmc)) {
                ret = mmc_regulator_set_vqmmc(mmc, ios);
-               if (ret) {
+               if (ret < 0) {
                        dev_err(host->dev, "Regulator set error %d\n", ret);
                        return ret;
                }
index bc5278ab5707c7c467337da8f375803a0b952b3b..35ae5737c62267f98fffa78126dce04f2d6b2ccf 100644 (file)
@@ -1546,8 +1546,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
 
        if (!IS_ERR(mmc->supply.vqmmc)) {
                ret = mmc_regulator_set_vqmmc(mmc, ios);
-
-               if (ret) {
+               if (ret < 0) {
                        dev_dbg(&mmc->class_dev,
                                         "Regulator set error %d - %s V\n",
                                         ret, uhs & v18 ? "1.8" : "3.3");
@@ -2752,12 +2751,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
        if (host->pdata->caps)
                mmc->caps = host->pdata->caps;
 
-       /*
-        * Support MMC_CAP_ERASE by default.
-        * It needs to use trim/discard/erase commands.
-        */
-       mmc->caps |= MMC_CAP_ERASE;
-
        if (host->pdata->pm_caps)
                mmc->pm_caps = host->pdata->pm_caps;
 
index fbae87d1f017e4ef70513514c3d2692626655137..cba7a6fcd178cc4591d2a5715aa715b2b98ddf60 100644 (file)
 #define        JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0)
 
 #define JZ_MMC_CLK_RATE 24000000
+#define JZ_MMC_REQ_TIMEOUT_MS 5000
 
 enum jz4740_mmc_version {
        JZ_MMC_JZ4740,
@@ -440,7 +441,8 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
 
        if (timeout == 0) {
                set_bit(0, &host->waiting);
-               mod_timer(&host->timeout_timer, jiffies + 5*HZ);
+               mod_timer(&host->timeout_timer,
+                         jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
                jz4740_mmc_set_irq_enabled(host, irq, true);
                return true;
        }
@@ -893,7 +895,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
 
        host->state = JZ4740_MMC_STATE_READ_RESPONSE;
        set_bit(0, &host->waiting);
-       mod_timer(&host->timeout_timer, jiffies + 5*HZ);
+       mod_timer(&host->timeout_timer,
+                 jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
        jz4740_mmc_send_command(host, req->cmd);
 }
 
@@ -1023,6 +1026,12 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
        mmc->f_min = mmc->f_max / 128;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
 
+       /*
+        * We use a fixed timeout of 5s, hence inform the core about it. A
+        * future improvement should instead respect the cmd->busy_timeout.
+        */
+       mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS;
+
        mmc->max_blk_size = (1 << 10) - 1;
        mmc->max_blk_count = (1 << 15) - 1;
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
index 35400cf2a2e4a59733a6fc0a630d0e69748866d9..7eb38d7482c6d97d6ef16024a13759ac7af1c635 100644 (file)
@@ -1004,6 +1004,8 @@ static int meson_mmc_card_busy(struct mmc_host *mmc)
 
 static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
 {
+       int ret;
+
        /* vqmmc regulator is available */
        if (!IS_ERR(mmc->supply.vqmmc)) {
                /*
@@ -1013,7 +1015,8 @@ static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
                 * to 1.8v. Please make sure the regulator framework is aware
                 * of your own regulator constraints
                 */
-               return mmc_regulator_set_vqmmc(mmc, ios);
+               ret = mmc_regulator_set_vqmmc(mmc, ios);
+               return ret < 0 ? ret : 0;
        }
 
        /* no vqmmc regulator, assume fixed regulator at 3/3.3V */
diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c
new file mode 100644 (file)
index 0000000..e1f29b2
--- /dev/null
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Amlogic Meson SDHC clock controller
+ *
+ * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "meson-mx-sdhc.h"
+
+#define MESON_SDHC_NUM_BUILTIN_CLKS    6
+
+struct meson_mx_sdhc_clkc {
+       struct clk_mux                  src_sel;
+       struct clk_divider              div;
+       struct clk_gate                 mod_clk_en;
+       struct clk_gate                 tx_clk_en;
+       struct clk_gate                 rx_clk_en;
+       struct clk_gate                 sd_clk_en;
+};
+
+static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
+       { .fw_name = "clkin0" },
+       { .fw_name = "clkin1" },
+       { .fw_name = "clkin2" },
+       { .fw_name = "clkin3" },
+};
+
+static const struct clk_div_table meson_mx_sdhc_div_table[] = {
+       { .div = 6, .val = 5, },
+       { .div = 8, .val = 7, },
+       { .div = 9, .val = 8, },
+       { .div = 10, .val = 9, },
+       { .div = 12, .val = 11, },
+       { .div = 16, .val = 15, },
+       { .div = 18, .val = 17, },
+       { .div = 34, .val = 33, },
+       { .div = 142, .val = 141, },
+       { .div = 850, .val = 849, },
+       { .div = 2126, .val = 2125, },
+       { .div = 4096, .val = 4095, },
+       { /* sentinel */ }
+};
+
+static int meson_mx_sdhc_clk_hw_register(struct device *dev,
+                                        const char *name_suffix,
+                                        const struct clk_parent_data *parents,
+                                        unsigned int num_parents,
+                                        const struct clk_ops *ops,
+                                        struct clk_hw *hw)
+{
+       struct clk_init_data init = { };
+       char clk_name[32];
+
+       snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
+                name_suffix);
+
+       init.name = clk_name;
+       init.ops = ops;
+       init.flags = CLK_SET_RATE_PARENT;
+       init.parent_data = parents;
+       init.num_parents = num_parents;
+
+       hw->init = &init;
+
+       return devm_clk_hw_register(dev, hw);
+}
+
+static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
+                                             const char *name_suffix,
+                                             struct clk_hw *parent,
+                                             struct clk_hw *hw)
+{
+       struct clk_parent_data parent_data = { .hw = parent };
+
+       return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
+                                            &clk_gate_ops, hw);
+}
+
+int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
+                               struct clk_bulk_data *clk_bulk_data)
+{
+       struct clk_parent_data div_parent = { };
+       struct meson_mx_sdhc_clkc *clkc_data;
+       int ret;
+
+       clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
+       if (!clkc_data)
+               return -ENOMEM;
+
+       clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
+       clkc_data->src_sel.mask = 0x3;
+       clkc_data->src_sel.shift = 16;
+       ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
+                                           meson_mx_sdhc_src_sel_parents, 4,
+                                           &clk_mux_ops,
+                                           &clkc_data->src_sel.hw);
+       if (ret)
+               return ret;
+
+       clkc_data->div.reg = base + MESON_SDHC_CLKC;
+       clkc_data->div.shift = 0;
+       clkc_data->div.width = 12;
+       clkc_data->div.table = meson_mx_sdhc_div_table;
+       div_parent.hw = &clkc_data->src_sel.hw;
+       ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
+                                           &clk_divider_ops,
+                                           &clkc_data->div.hw);
+       if (ret)
+               return ret;
+
+       clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
+       clkc_data->mod_clk_en.bit_idx = 15;
+       ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
+                                                &clkc_data->div.hw,
+                                                &clkc_data->mod_clk_en.hw);
+       if (ret)
+               return ret;
+
+       clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
+       clkc_data->tx_clk_en.bit_idx = 14;
+       ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
+                                                &clkc_data->div.hw,
+                                                &clkc_data->tx_clk_en.hw);
+       if (ret)
+               return ret;
+
+       clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
+       clkc_data->rx_clk_en.bit_idx = 13;
+       ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
+                                                &clkc_data->div.hw,
+                                                &clkc_data->rx_clk_en.hw);
+       if (ret)
+               return ret;
+
+       clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
+       clkc_data->sd_clk_en.bit_idx = 12;
+       ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
+                                                &clkc_data->div.hw,
+                                                &clkc_data->sd_clk_en.hw);
+       if (ret)
+               return ret;
+
+       /*
+        * TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
+        * available.
+        */
+       clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
+       clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
+       clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
+       clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
+
+       return 0;
+}
diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c
new file mode 100644 (file)
index 0000000..53e3f6a
--- /dev/null
@@ -0,0 +1,914 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Amlogic Meson6/Meson8/Meson8b/Meson8m2 SDHC MMC host controller driver.
+ *
+ * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+
+#include "meson-mx-sdhc.h"
+
+#define MESON_SDHC_NUM_BULK_CLKS                               4
+#define MESON_SDHC_MAX_BLK_SIZE                                        512
+#define MESON_SDHC_NUM_TUNING_TRIES                            10
+
+#define MESON_SDHC_WAIT_CMD_READY_SLEEP_US                     1
+#define MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US                   100000
+#define MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US                   1
+#define MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US                 200
+
+struct meson_mx_sdhc_data {
+       void            (*init_hw)(struct mmc_host *mmc);
+       void            (*set_pdma)(struct mmc_host *mmc);
+       void            (*wait_before_send)(struct mmc_host *mmc);
+       bool            hardware_flush_all_cmds;
+};
+
+struct meson_mx_sdhc_host {
+       struct mmc_host                 *mmc;
+
+       struct mmc_request              *mrq;
+       struct mmc_command              *cmd;
+       int                             error;
+
+       struct regmap                   *regmap;
+
+       struct clk                      *pclk;
+       struct clk                      *sd_clk;
+       struct clk_bulk_data            bulk_clks[MESON_SDHC_NUM_BULK_CLKS];
+       bool                            bulk_clks_enabled;
+
+       const struct meson_mx_sdhc_data *platform;
+};
+
+static const struct regmap_config meson_mx_sdhc_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = MESON_SDHC_CLK2,
+};
+
+static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL |
+                    MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
+                    MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX |
+                    MESON_SDHC_SRST_DMA_IF);
+       usleep_range(10, 100);
+
+       regmap_write(host->regmap, MESON_SDHC_SRST, 0);
+       usleep_range(10, 100);
+}
+
+static void meson_mx_sdhc_clear_fifo(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 stat;
+
+       regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
+       if (!FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) &&
+           !FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
+               return;
+
+       regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_RXFIFO |
+                    MESON_SDHC_SRST_TXFIFO | MESON_SDHC_SRST_MAIN_CTRL);
+       udelay(5);
+
+       regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
+       if (FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) ||
+           FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
+               dev_warn(mmc_dev(host->mmc),
+                        "Failed to clear FIFOs, RX: %lu, TX: %lu\n",
+                        FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat),
+                        FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat));
+}
+
+static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 stat, esta;
+       int ret;
+
+       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
+                                      !(stat & MESON_SDHC_STAT_CMD_BUSY),
+                                      MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
+                                      MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
+       if (ret) {
+               dev_warn(mmc_dev(mmc),
+                        "Failed to poll for CMD_BUSY while processing CMD%d\n",
+                        host->cmd->opcode);
+               meson_mx_sdhc_hw_reset(mmc);
+       }
+
+       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
+                                      !(esta & MESON_SDHC_ESTA_11_13),
+                                      MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
+                                      MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
+       if (ret) {
+               dev_warn(mmc_dev(mmc),
+                        "Failed to poll for ESTA[13:11] while processing CMD%d\n",
+                        host->cmd->opcode);
+               meson_mx_sdhc_hw_reset(mmc);
+       }
+}
+
+static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
+                                   struct mmc_command *cmd)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 ictl, send;
+       int pack_len;
+
+       host->cmd = cmd;
+
+       ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
+              MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
+              MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
+
+       send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
+
+       if (cmd->data) {
+               send |= MESON_SDHC_SEND_CMD_HAS_DATA;
+               send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
+                                  cmd->data->blocks - 1);
+
+               if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
+                       pack_len = cmd->data->blksz;
+               else
+                       pack_len = 0;
+
+               if (cmd->data->flags & MMC_DATA_WRITE)
+                       send |= MESON_SDHC_SEND_DATA_DIR;
+
+               /*
+                * If command with no data, just wait response done
+                * interrupt(int[0]), and if command with data transfer, just
+                * wait dma done interrupt(int[11]), don't need care about
+                * dat0 busy or not.
+                */
+               if (host->platform->hardware_flush_all_cmds ||
+                   cmd->data->flags & MMC_DATA_WRITE)
+                       /* hardware flush: */
+                       ictl |= MESON_SDHC_ICTL_DMA_DONE;
+               else
+                       /* software flush: */
+                       ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
+       } else {
+               pack_len = 0;
+
+               ictl |= MESON_SDHC_ICTL_RESP_OK;
+       }
+
+       if (cmd->opcode == MMC_STOP_TRANSMISSION)
+               send |= MESON_SDHC_SEND_DATA_STOP;
+
+       if (cmd->flags & MMC_RSP_PRESENT)
+               send |= MESON_SDHC_SEND_CMD_HAS_RESP;
+
+       if (cmd->flags & MMC_RSP_136) {
+               send |= MESON_SDHC_SEND_RESP_LEN;
+               send |= MESON_SDHC_SEND_RESP_NO_CRC;
+       }
+
+       if (!(cmd->flags & MMC_RSP_CRC))
+               send |= MESON_SDHC_SEND_RESP_NO_CRC;
+
+       if (cmd->flags & MMC_RSP_BUSY)
+               send |= MESON_SDHC_SEND_R1B;
+
+       /* enable the new IRQs and mask all pending ones */
+       regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
+       regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
+
+       regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
+
+       regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
+                          MESON_SDHC_CTRL_PACK_LEN,
+                          FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
+
+       if (cmd->data)
+               regmap_write(host->regmap, MESON_SDHC_ADDR,
+                            sg_dma_address(cmd->data->sg));
+
+       meson_mx_sdhc_wait_cmd_ready(mmc);
+
+       if (cmd->data)
+               host->platform->set_pdma(mmc);
+
+       if (host->platform->wait_before_send)
+               host->platform->wait_before_send(mmc);
+
+       regmap_write(host->regmap, MESON_SDHC_SEND, send);
+}
+
+static void meson_mx_sdhc_disable_clks(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       if (!host->bulk_clks_enabled)
+               return;
+
+       clk_bulk_disable_unprepare(MESON_SDHC_NUM_BULK_CLKS, host->bulk_clks);
+
+       host->bulk_clks_enabled = false;
+}
+
+static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       int ret;
+
+       if (host->bulk_clks_enabled)
+               return 0;
+
+       ret = clk_bulk_prepare_enable(MESON_SDHC_NUM_BULK_CLKS,
+                                     host->bulk_clks);
+       if (ret)
+               return ret;
+
+       host->bulk_clks_enabled = true;
+
+       return 0;
+}
+
+static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 rx_clk_phase;
+       int ret;
+
+       meson_mx_sdhc_disable_clks(mmc);
+
+       if (ios->clock) {
+               ret = clk_set_rate(host->sd_clk, ios->clock);
+               if (ret) {
+                       dev_warn(mmc_dev(mmc),
+                                "Failed to set MMC clock to %uHz: %d\n",
+                                ios->clock, host->error);
+                       return ret;
+               }
+
+               ret = meson_mx_sdhc_enable_clks(mmc);
+               if (ret)
+                       return ret;
+
+               mmc->actual_clock = clk_get_rate(host->sd_clk);
+
+               /*
+                * according to Amlogic the following latching points are
+                * selected with empirical values, there is no (known) formula
+                * to calculate these.
+                */
+               if (mmc->actual_clock > 100000000) {
+                       rx_clk_phase = 1;
+               } else if (mmc->actual_clock > 45000000) {
+                       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+                               rx_clk_phase = 15;
+                       else
+                               rx_clk_phase = 11;
+               } else if (mmc->actual_clock >= 25000000) {
+                       rx_clk_phase = 15;
+               } else if (mmc->actual_clock > 5000000) {
+                       rx_clk_phase = 23;
+               } else if (mmc->actual_clock > 1000000) {
+                       rx_clk_phase = 55;
+               } else {
+                       rx_clk_phase = 1061;
+               }
+
+               regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
+                                  MESON_SDHC_CLK2_RX_CLK_PHASE,
+                                  FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
+                                             rx_clk_phase));
+       } else {
+               mmc->actual_clock = 0;
+       }
+
+       return 0;
+}
+
+static void meson_mx_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       unsigned short vdd = ios->vdd;
+
+       switch (ios->power_mode) {
+       case MMC_POWER_OFF:
+               vdd = 0;
+               fallthrough;
+
+       case MMC_POWER_UP:
+               if (!IS_ERR(mmc->supply.vmmc)) {
+                       host->error = mmc_regulator_set_ocr(mmc,
+                                                           mmc->supply.vmmc,
+                                                           vdd);
+                       if (host->error)
+                               return;
+               }
+
+               break;
+
+       case MMC_POWER_ON:
+               break;
+       }
+
+       host->error = meson_mx_sdhc_set_clk(mmc, ios);
+       if (host->error)
+               return;
+
+       switch (ios->bus_width) {
+       case MMC_BUS_WIDTH_1:
+               regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
+                                  MESON_SDHC_CTRL_DAT_TYPE,
+                                  FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 0));
+               break;
+
+       case MMC_BUS_WIDTH_4:
+               regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
+                                  MESON_SDHC_CTRL_DAT_TYPE,
+                                  FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 1));
+               break;
+
+       case MMC_BUS_WIDTH_8:
+               regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
+                                  MESON_SDHC_CTRL_DAT_TYPE,
+                                  FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 2));
+               break;
+
+       default:
+               dev_err(mmc_dev(mmc), "unsupported bus width: %d\n",
+                       ios->bus_width);
+               host->error = -EINVAL;
+               return;
+       }
+}
+
+static int meson_mx_sdhc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct mmc_data *data = mrq->data;
+       int dma_len;
+
+       if (!data)
+               return 0;
+
+       dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
+                            mmc_get_dma_dir(data));
+       if (dma_len <= 0) {
+               dev_err(mmc_dev(mmc), "dma_map_sg failed\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void meson_mx_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       struct mmc_command *cmd = mrq->cmd;
+
+       if (!host->error)
+               host->error = meson_mx_sdhc_map_dma(mmc, mrq);
+
+       if (host->error) {
+               cmd->error = host->error;
+               mmc_request_done(mmc, mrq);
+               return;
+       }
+
+       host->mrq = mrq;
+
+       meson_mx_sdhc_start_cmd(mmc, mrq->cmd);
+}
+
+static int meson_mx_sdhc_card_busy(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 stat;
+
+       regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
+       return FIELD_GET(MESON_SDHC_STAT_DAT3_0, stat) == 0;
+}
+
+static bool meson_mx_sdhc_tuning_point_matches(struct mmc_host *mmc,
+                                              u32 opcode)
+{
+       unsigned int i, num_matches = 0;
+       int ret;
+
+       for (i = 0; i < MESON_SDHC_NUM_TUNING_TRIES; i++) {
+               ret = mmc_send_tuning(mmc, opcode, NULL);
+               if (!ret)
+                       num_matches++;
+       }
+
+       return num_matches == MESON_SDHC_NUM_TUNING_TRIES;
+}
+
+static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       int div, start, len, best_start, best_len;
+       int curr_phase, old_phase, new_phase;
+       u32 val;
+
+       len = 0;
+       start = 0;
+       best_len = 0;
+
+       regmap_read(host->regmap, MESON_SDHC_CLK2, &val);
+       old_phase = FIELD_GET(MESON_SDHC_CLK2_RX_CLK_PHASE, val);
+
+       regmap_read(host->regmap, MESON_SDHC_CLKC, &val);
+       div = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val);
+
+       for (curr_phase = 0; curr_phase <= div; curr_phase++) {
+               regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
+                                  MESON_SDHC_CLK2_RX_CLK_PHASE,
+                                  FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
+                                             curr_phase));
+
+               if (meson_mx_sdhc_tuning_point_matches(mmc, opcode)) {
+                       if (!len) {
+                               start = curr_phase;
+
+                               dev_dbg(mmc_dev(mmc),
+                                       "New RX phase window starts at %u\n",
+                                       start);
+                       }
+
+                       len++;
+               } else {
+                       if (len > best_len) {
+                               best_start = start;
+                               best_len = len;
+
+                               dev_dbg(mmc_dev(mmc),
+                                       "New best RX phase window: %u - %u\n",
+                                       best_start, best_start + best_len);
+                       }
+
+                       /* reset the current window */
+                       len = 0;
+               }
+       }
+
+       if (len > best_len)
+               /* the last window is the best (or possibly only) window */
+               new_phase = start + (len / 2);
+       else if (best_len)
+               /* there was a better window than the last */
+               new_phase = best_start + (best_len / 2);
+       else
+               /* no window was found at all, reset to the original phase */
+               new_phase = old_phase;
+
+       regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
+                          MESON_SDHC_CLK2_RX_CLK_PHASE,
+                          FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
+                                     new_phase));
+
+       if (!len && !best_len)
+               return -EIO;
+
+       dev_dbg(mmc_dev(mmc), "Tuned RX clock phase to %u\n", new_phase);
+
+       return 0;
+}
+
+static const struct mmc_host_ops meson_mx_sdhc_ops = {
+       .hw_reset                       = meson_mx_sdhc_hw_reset,
+       .request                        = meson_mx_sdhc_request,
+       .set_ios                        = meson_mx_sdhc_set_ios,
+       .card_busy                      = meson_mx_sdhc_card_busy,
+       .execute_tuning                 = meson_mx_sdhc_execute_tuning,
+       .get_cd                         = mmc_gpio_get_cd,
+       .get_ro                         = mmc_gpio_get_ro,
+};
+
+static void meson_mx_sdhc_request_done(struct meson_mx_sdhc_host *host)
+{
+       struct mmc_request *mrq = host->mrq;
+       struct mmc_host *mmc = host->mmc;
+
+       /* disable interrupts and mask all pending ones */
+       regmap_update_bits(host->regmap, MESON_SDHC_ICTL,
+                          MESON_SDHC_ICTL_ALL_IRQS, 0);
+       regmap_update_bits(host->regmap, MESON_SDHC_ISTA,
+                          MESON_SDHC_ISTA_ALL_IRQS, MESON_SDHC_ISTA_ALL_IRQS);
+
+       host->mrq = NULL;
+       host->cmd = NULL;
+
+       mmc_request_done(mmc, mrq);
+}
+
+static u32 meson_mx_sdhc_read_response(struct meson_mx_sdhc_host *host, u8 idx)
+{
+       u32 val;
+
+       regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                          MESON_SDHC_PDMA_DMA_MODE, 0);
+
+       regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                          MESON_SDHC_PDMA_PIO_RDRESP,
+                          FIELD_PREP(MESON_SDHC_PDMA_PIO_RDRESP, idx));
+
+       regmap_read(host->regmap, MESON_SDHC_ARGU, &val);
+
+       return val;
+}
+
+static irqreturn_t meson_mx_sdhc_irq(int irq, void *data)
+{
+       struct meson_mx_sdhc_host *host = data;
+       struct mmc_command *cmd = host->cmd;
+       u32 ictl, ista;
+
+       regmap_read(host->regmap, MESON_SDHC_ICTL, &ictl);
+       regmap_read(host->regmap, MESON_SDHC_ISTA, &ista);
+
+       if (!(ictl & ista))
+               return IRQ_NONE;
+
+       if (ista & MESON_SDHC_ISTA_RXFIFO_FULL ||
+           ista & MESON_SDHC_ISTA_TXFIFO_EMPTY)
+               cmd->error = -EIO;
+       else if (ista & MESON_SDHC_ISTA_RESP_ERR_CRC)
+               cmd->error = -EILSEQ;
+       else if (ista & MESON_SDHC_ISTA_RESP_TIMEOUT)
+               cmd->error = -ETIMEDOUT;
+
+       if (cmd->data) {
+               if (ista & MESON_SDHC_ISTA_DATA_ERR_CRC)
+                       cmd->data->error = -EILSEQ;
+               else if (ista & MESON_SDHC_ISTA_DATA_TIMEOUT)
+                       cmd->data->error = -ETIMEDOUT;
+       }
+
+       if (cmd->error || (cmd->data && cmd->data->error))
+               dev_dbg(mmc_dev(host->mmc), "CMD%d error, ISTA: 0x%08x\n",
+                       cmd->opcode, ista);
+
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data)
+{
+       struct meson_mx_sdhc_host *host = irq_data;
+       struct mmc_command *cmd;
+       u32 val;
+
+       cmd = host->cmd;
+       if (WARN_ON(!cmd))
+               return IRQ_HANDLED;
+
+       if (cmd->data && !cmd->data->error) {
+               if (!host->platform->hardware_flush_all_cmds &&
+                   cmd->data->flags & MMC_DATA_READ) {
+                       meson_mx_sdhc_wait_cmd_ready(host->mmc);
+
+                       /*
+                        * If MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH was
+                        * previously 0x1 then it has to be set to 0x3. If it
+                        * was 0x0 before then it has to be set to 0x2. Without
+                        * this reading SD cards sometimes transfers garbage,
+                        * which results in cards not being detected due to:
+                        *   unrecognised SCR structure version <random number>
+                        */
+                       val = FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
+                                        2);
+                       regmap_update_bits(host->regmap, MESON_SDHC_PDMA, val,
+                                          val);
+               }
+
+               dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
+                            cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
+
+               cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
+       }
+
+       meson_mx_sdhc_wait_cmd_ready(host->mmc);
+
+       if (cmd->flags & MMC_RSP_136) {
+               cmd->resp[0] = meson_mx_sdhc_read_response(host, 4);
+               cmd->resp[1] = meson_mx_sdhc_read_response(host, 3);
+               cmd->resp[2] = meson_mx_sdhc_read_response(host, 2);
+               cmd->resp[3] = meson_mx_sdhc_read_response(host, 1);
+       } else {
+               cmd->resp[0] = meson_mx_sdhc_read_response(host, 0);
+       }
+
+       if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
+               meson_mx_sdhc_hw_reset(host->mmc);
+       else if (cmd->data)
+               /*
+                * Clear the FIFOs after completing data transfers to prevent
+                * corrupting data on write access. It's not clear why this is
+                * needed (for reads and writes), but it mimics what the BSP
+                * kernel did.
+                */
+               meson_mx_sdhc_clear_fifo(host->mmc);
+
+       meson_mx_sdhc_request_done(host);
+
+       return IRQ_HANDLED;
+}
+
+static void meson_mx_sdhc_init_hw_meson8(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       regmap_write(host->regmap, MESON_SDHC_MISC,
+                    FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 7) |
+                    FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
+                    FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
+
+       regmap_write(host->regmap, MESON_SDHC_ENHC,
+                    FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 63) |
+                    MESON_SDHC_ENHC_MESON6_DMA_WR_RESP |
+                    FIELD_PREP(MESON_SDHC_ENHC_MESON6_RX_TIMEOUT, 255) |
+                    FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
+};
+
+static void meson_mx_sdhc_set_pdma_meson8(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       if (host->cmd->data->flags & MMC_DATA_WRITE)
+               regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                                  MESON_SDHC_PDMA_DMA_MODE |
+                                  MESON_SDHC_PDMA_RD_BURST |
+                                  MESON_SDHC_PDMA_TXFIFO_FILL,
+                                  MESON_SDHC_PDMA_DMA_MODE |
+                                  FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 31) |
+                                  MESON_SDHC_PDMA_TXFIFO_FILL);
+       else
+               regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                                  MESON_SDHC_PDMA_DMA_MODE |
+                                  MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
+                                  MESON_SDHC_PDMA_DMA_MODE |
+                                  FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
+                                             1));
+
+       if (host->cmd->data->flags & MMC_DATA_WRITE)
+               regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                                  MESON_SDHC_PDMA_RD_BURST,
+                                  FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15));
+}
+
+static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+       u32 val;
+       int ret;
+
+       ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
+                                      val == 0,
+                                      MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
+                                      MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
+       if (ret)
+               dev_warn(mmc_dev(mmc),
+                        "Failed to wait for ESTA to clear: 0x%08x\n", val);
+
+       if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
+               ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
+                                       val, val & MESON_SDHC_STAT_TXFIFO_CNT,
+                                       MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
+                                       MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
+               if (ret)
+                       dev_warn(mmc_dev(mmc),
+                                "Failed to wait for TX FIFO to fill\n");
+       }
+}
+
+static void meson_mx_sdhc_init_hw_meson8m2(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       regmap_write(host->regmap, MESON_SDHC_MISC,
+                    FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 6) |
+                    FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
+                    FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
+
+       regmap_write(host->regmap, MESON_SDHC_ENHC,
+                    FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 64) |
+                    FIELD_PREP(MESON_SDHC_ENHC_MESON8M2_DEBUG, 1) |
+                    MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE |
+                    FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
+}
+
+static void meson_mx_sdhc_set_pdma_meson8m2(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
+                          MESON_SDHC_PDMA_DMA_MODE, MESON_SDHC_PDMA_DMA_MODE);
+}
+
+static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
+{
+       struct meson_mx_sdhc_host *host = mmc_priv(mmc);
+
+       meson_mx_sdhc_hw_reset(mmc);
+
+       regmap_write(host->regmap, MESON_SDHC_CTRL,
+                    FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |
+                    FIELD_PREP(MESON_SDHC_CTRL_RX_TIMEOUT, 0x7f) |
+                    FIELD_PREP(MESON_SDHC_CTRL_RX_ENDIAN, 0x7) |
+                    FIELD_PREP(MESON_SDHC_CTRL_TX_ENDIAN, 0x7));
+
+       /*
+        * start with a valid divider and enable the memory (un-setting
+        * MESON_SDHC_CLKC_MEM_PWR_OFF).
+        */
+       regmap_write(host->regmap, MESON_SDHC_CLKC, MESON_SDHC_CLKC_CLK_DIV);
+
+       regmap_write(host->regmap, MESON_SDHC_CLK2,
+                    FIELD_PREP(MESON_SDHC_CLK2_SD_CLK_PHASE, 1));
+
+       regmap_write(host->regmap, MESON_SDHC_PDMA,
+                    MESON_SDHC_PDMA_DMA_URGENT |
+                    FIELD_PREP(MESON_SDHC_PDMA_WR_BURST, 7) |
+                    FIELD_PREP(MESON_SDHC_PDMA_TXFIFO_TH, 49) |
+                    FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15) |
+                    FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_TH, 7));
+
+       /* some initialization bits depend on the SoC: */
+       host->platform->init_hw(mmc);
+
+       /* disable and mask all interrupts: */
+       regmap_write(host->regmap, MESON_SDHC_ICTL, 0);
+       regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
+}
+
+static int meson_mx_sdhc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct meson_mx_sdhc_host *host;
+       struct mmc_host *mmc;
+       void __iomem *base;
+       int ret, irq;
+
+       mmc = mmc_alloc_host(sizeof(*host), dev);
+       if (!mmc)
+               return -ENOMEM;
+
+       ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
+                                      mmc);
+       if (ret) {
+               dev_err(dev, "Failed to register mmc_free_host action\n");
+               return ret;
+       }
+
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+
+       platform_set_drvdata(pdev, host);
+
+       host->platform = device_get_match_data(dev);
+       if (!host->platform)
+               return -EINVAL;
+
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       host->regmap = devm_regmap_init_mmio(dev, base,
+                                            &meson_mx_sdhc_regmap_config);
+       if (IS_ERR(host->regmap))
+               return PTR_ERR(host->regmap);
+
+       host->pclk = devm_clk_get(dev, "pclk");
+       if (IS_ERR(host->pclk))
+               return PTR_ERR(host->pclk);
+
+       /* accessing any register requires the module clock to be enabled: */
+       ret = clk_prepare_enable(host->pclk);
+       if (ret) {
+               dev_err(dev, "Failed to enable 'pclk' clock\n");
+               return ret;
+       }
+
+       meson_mx_sdhc_init_hw(mmc);
+
+       ret = meson_mx_sdhc_register_clkc(dev, base, host->bulk_clks);
+       if (ret)
+               goto err_disable_pclk;
+
+       host->sd_clk = host->bulk_clks[1].clk;
+
+       /* Get regulators and the supported OCR mask */
+       ret = mmc_regulator_get_supply(mmc);
+       if (ret)
+               goto err_disable_pclk;
+
+       mmc->max_req_size = SZ_128K;
+       mmc->max_seg_size = mmc->max_req_size;
+       mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
+       mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
+       mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
+       mmc->f_min = clk_round_rate(host->sd_clk, 1);
+       mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
+       mmc->max_current_180 = 300;
+       mmc->max_current_330 = 300;
+       mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_HW_RESET;
+       mmc->ops = &meson_mx_sdhc_ops;
+
+       ret = mmc_of_parse(mmc);
+       if (ret)
+               goto err_disable_pclk;
+
+       irq = platform_get_irq(pdev, 0);
+       ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
+                                       meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
+                                       NULL, host);
+       if (ret)
+               goto err_disable_pclk;
+
+       ret = mmc_add_host(mmc);
+       if (ret)
+               goto err_disable_pclk;
+
+       return 0;
+
+err_disable_pclk:
+       clk_disable_unprepare(host->pclk);
+       return ret;
+}
+
+static int meson_mx_sdhc_remove(struct platform_device *pdev)
+{
+       struct meson_mx_sdhc_host *host = platform_get_drvdata(pdev);
+
+       mmc_remove_host(host->mmc);
+
+       meson_mx_sdhc_disable_clks(host->mmc);
+
+       clk_disable_unprepare(host->pclk);
+
+       return 0;
+}
+
+static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8 = {
+       .init_hw                        = meson_mx_sdhc_init_hw_meson8,
+       .set_pdma                       = meson_mx_sdhc_set_pdma_meson8,
+       .wait_before_send               = meson_mx_sdhc_wait_before_send_meson8,
+       .hardware_flush_all_cmds        = false,
+};
+
+static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8m2 = {
+       .init_hw                        = meson_mx_sdhc_init_hw_meson8m2,
+       .set_pdma                       = meson_mx_sdhc_set_pdma_meson8m2,
+       .hardware_flush_all_cmds        = true,
+};
+
+static const struct of_device_id meson_mx_sdhc_of_match[] = {
+       {
+               .compatible = "amlogic,meson8-sdhc",
+               .data = &meson_mx_sdhc_data_meson8
+       },
+       {
+               .compatible = "amlogic,meson8b-sdhc",
+               .data = &meson_mx_sdhc_data_meson8
+       },
+       {
+               .compatible = "amlogic,meson8m2-sdhc",
+               .data = &meson_mx_sdhc_data_meson8m2
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, meson_mx_sdhc_of_match);
+
+static struct platform_driver meson_mx_sdhc_driver = {
+       .probe   = meson_mx_sdhc_probe,
+       .remove  = meson_mx_sdhc_remove,
+       .driver  = {
+               .name = "meson-mx-sdhc",
+               .of_match_table = of_match_ptr(meson_mx_sdhc_of_match),
+       },
+};
+
+module_platform_driver(meson_mx_sdhc_driver);
+
+MODULE_DESCRIPTION("Meson6, Meson8, Meson8b and Meson8m2 SDHC Host Driver");
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/meson-mx-sdhc.h b/drivers/mmc/host/meson-mx-sdhc.h
new file mode 100644 (file)
index 0000000..230e8fb
--- /dev/null
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#ifndef _MESON_MX_SDHC_H_
+#define _MESON_MX_SDHC_H_
+
+#include <linux/bitfield.h>
+
+#define MESON_SDHC_ARGU                                                0x00
+
+#define MESON_SDHC_SEND                                                0x04
+       #define MESON_SDHC_SEND_CMD_INDEX                       GENMASK(5, 0)
+       #define MESON_SDHC_SEND_CMD_HAS_RESP                    BIT(6)
+       #define MESON_SDHC_SEND_CMD_HAS_DATA                    BIT(7)
+       #define MESON_SDHC_SEND_RESP_LEN                        BIT(8)
+       #define MESON_SDHC_SEND_RESP_NO_CRC                     BIT(9)
+       #define MESON_SDHC_SEND_DATA_DIR                        BIT(10)
+       #define MESON_SDHC_SEND_DATA_STOP                       BIT(11)
+       #define MESON_SDHC_SEND_R1B                             BIT(12)
+       #define MESON_SDHC_SEND_TOTAL_PACK                      GENMASK(31, 16)
+
+#define MESON_SDHC_CTRL                                                0x08
+       #define MESON_SDHC_CTRL_DAT_TYPE                        GENMASK(1, 0)
+       #define MESON_SDHC_CTRL_DDR_MODE                        BIT(2)
+       #define MESON_SDHC_CTRL_TX_CRC_NOCHECK                  BIT(3)
+       #define MESON_SDHC_CTRL_PACK_LEN                        GENMASK(12, 4)
+       #define MESON_SDHC_CTRL_RX_TIMEOUT                      GENMASK(19, 13)
+       #define MESON_SDHC_CTRL_RX_PERIOD                       GENMASK(23, 20)
+       #define MESON_SDHC_CTRL_RX_ENDIAN                       GENMASK(26, 24)
+       #define MESON_SDHC_CTRL_SDIO_IRQ_MODE                   BIT(27)
+       #define MESON_SDHC_CTRL_DAT0_IRQ_SEL                    BIT(28)
+       #define MESON_SDHC_CTRL_TX_ENDIAN                       GENMASK(31, 29)
+
+#define MESON_SDHC_STAT                                                0x0c
+       #define MESON_SDHC_STAT_CMD_BUSY                        BIT(0)
+       #define MESON_SDHC_STAT_DAT3_0                          GENMASK(4, 1)
+       #define MESON_SDHC_STAT_CMD                             BIT(5)
+       #define MESON_SDHC_STAT_RXFIFO_CNT                      GENMASK(12, 6)
+       #define MESON_SDHC_STAT_TXFIFO_CNT                      GENMASK(19, 13)
+       #define MESON_SDHC_STAT_DAT7_4                          GENMASK(23, 20)
+
+#define MESON_SDHC_CLKC                                                0x10
+       #define MESON_SDHC_CLKC_CLK_DIV                         GENMASK(11, 0)
+       #define MESON_SDHC_CLKC_CLK_JIC                         BIT(24)
+       #define MESON_SDHC_CLKC_MEM_PWR_OFF                     GENMASK(26, 25)
+
+#define MESON_SDHC_ADDR                                                0x14
+
+#define MESON_SDHC_PDMA                                                0x18
+       #define MESON_SDHC_PDMA_DMA_MODE                        BIT(0)
+       #define MESON_SDHC_PDMA_PIO_RDRESP                      GENMASK(3, 1)
+       #define MESON_SDHC_PDMA_DMA_URGENT                      BIT(4)
+       #define MESON_SDHC_PDMA_WR_BURST                        GENMASK(9, 5)
+       #define MESON_SDHC_PDMA_RD_BURST                        GENMASK(14, 10)
+       #define MESON_SDHC_PDMA_RXFIFO_TH                       GENMASK(21, 15)
+       #define MESON_SDHC_PDMA_TXFIFO_TH                       GENMASK(28, 22)
+       #define MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH             GENMASK(30, 29)
+       #define MESON_SDHC_PDMA_TXFIFO_FILL                     BIT(31)
+
+#define MESON_SDHC_MISC                                                0x1c
+       #define MESON_SDHC_MISC_WCRC_ERR_PATT                   GENMASK(6, 4)
+       #define MESON_SDHC_MISC_WCRC_OK_PATT                    GENMASK(9, 7)
+       #define MESON_SDHC_MISC_BURST_NUM                       GENMASK(21, 16)
+       #define MESON_SDHC_MISC_THREAD_ID                       GENMASK(27, 22)
+       #define MESON_SDHC_MISC_MANUAL_STOP                     BIT(28)
+       #define MESON_SDHC_MISC_TXSTART_THRES                   GENMASK(31, 29)
+
+#define MESON_SDHC_DATA                                                0x20
+
+#define MESON_SDHC_ICTL                                                0x24
+       #define MESON_SDHC_ICTL_RESP_OK                         BIT(0)
+       #define MESON_SDHC_ICTL_RESP_TIMEOUT                    BIT(1)
+       #define MESON_SDHC_ICTL_RESP_ERR_CRC                    BIT(2)
+       #define MESON_SDHC_ICTL_RESP_OK_NOCLEAR                 BIT(3)
+       #define MESON_SDHC_ICTL_DATA_1PACK_OK                   BIT(4)
+       #define MESON_SDHC_ICTL_DATA_TIMEOUT                    BIT(5)
+       #define MESON_SDHC_ICTL_DATA_ERR_CRC                    BIT(6)
+       #define MESON_SDHC_ICTL_DATA_XFER_OK                    BIT(7)
+       #define MESON_SDHC_ICTL_RX_HIGHER                       BIT(8)
+       #define MESON_SDHC_ICTL_RX_LOWER                        BIT(9)
+       #define MESON_SDHC_ICTL_DAT1_IRQ                        BIT(10)
+       #define MESON_SDHC_ICTL_DMA_DONE                        BIT(11)
+       #define MESON_SDHC_ICTL_RXFIFO_FULL                     BIT(12)
+       #define MESON_SDHC_ICTL_TXFIFO_EMPTY                    BIT(13)
+       #define MESON_SDHC_ICTL_ADDI_DAT1_IRQ                   BIT(14)
+       #define MESON_SDHC_ICTL_ALL_IRQS                        GENMASK(14, 0)
+       #define MESON_SDHC_ICTL_DAT1_IRQ_DELAY                  GENMASK(17, 16)
+
+#define MESON_SDHC_ISTA                                                0x28
+       #define MESON_SDHC_ISTA_RESP_OK                         BIT(0)
+       #define MESON_SDHC_ISTA_RESP_TIMEOUT                    BIT(1)
+       #define MESON_SDHC_ISTA_RESP_ERR_CRC                    BIT(2)
+       #define MESON_SDHC_ISTA_RESP_OK_NOCLEAR                 BIT(3)
+       #define MESON_SDHC_ISTA_DATA_1PACK_OK                   BIT(4)
+       #define MESON_SDHC_ISTA_DATA_TIMEOUT                    BIT(5)
+       #define MESON_SDHC_ISTA_DATA_ERR_CRC                    BIT(6)
+       #define MESON_SDHC_ISTA_DATA_XFER_OK                    BIT(7)
+       #define MESON_SDHC_ISTA_RX_HIGHER                       BIT(8)
+       #define MESON_SDHC_ISTA_RX_LOWER                        BIT(9)
+       #define MESON_SDHC_ISTA_DAT1_IRQ                        BIT(10)
+       #define MESON_SDHC_ISTA_DMA_DONE                        BIT(11)
+       #define MESON_SDHC_ISTA_RXFIFO_FULL                     BIT(12)
+       #define MESON_SDHC_ISTA_TXFIFO_EMPTY                    BIT(13)
+       #define MESON_SDHC_ISTA_ADDI_DAT1_IRQ                   BIT(14)
+       #define MESON_SDHC_ISTA_ALL_IRQS                        GENMASK(14, 0)
+
+#define MESON_SDHC_SRST                                                0x2c
+       #define MESON_SDHC_SRST_MAIN_CTRL                       BIT(0)
+       #define MESON_SDHC_SRST_RXFIFO                          BIT(1)
+       #define MESON_SDHC_SRST_TXFIFO                          BIT(2)
+       #define MESON_SDHC_SRST_DPHY_RX                         BIT(3)
+       #define MESON_SDHC_SRST_DPHY_TX                         BIT(4)
+       #define MESON_SDHC_SRST_DMA_IF                          BIT(5)
+
+#define MESON_SDHC_ESTA                                                0x30
+       #define MESON_SDHC_ESTA_11_13                           GENMASK(13, 11)
+
+#define MESON_SDHC_ENHC                                                0x34
+       #define MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE             BIT(0)
+       #define MESON_SDHC_ENHC_MESON8M2_CHK_WRRSP              BIT(1)
+       #define MESON_SDHC_ENHC_MESON8M2_CHK_DMA                BIT(2)
+       #define MESON_SDHC_ENHC_MESON8M2_DEBUG                  GENMASK(5, 3)
+       #define MESON_SDHC_ENHC_MESON6_RX_TIMEOUT               GENMASK(7, 0)
+       #define MESON_SDHC_ENHC_MESON6_DMA_RD_RESP              BIT(16)
+       #define MESON_SDHC_ENHC_MESON6_DMA_WR_RESP              BIT(17)
+       #define MESON_SDHC_ENHC_SDIO_IRQ_PERIOD                 GENMASK(15, 8)
+       #define MESON_SDHC_ENHC_RXFIFO_TH                       GENMASK(24, 18)
+       #define MESON_SDHC_ENHC_TXFIFO_TH                       GENMASK(31, 25)
+
+#define MESON_SDHC_CLK2                                                0x38
+       #define MESON_SDHC_CLK2_RX_CLK_PHASE                    GENMASK(11, 0)
+       #define MESON_SDHC_CLK2_SD_CLK_PHASE                    GENMASK(23, 12)
+
+struct clk_bulk_data;
+
+int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
+                               struct clk_bulk_data *clk_bulk_data);
+
+#endif /* _MESON_MX_SDHC_H_ */
index 2e58743d83bb5feb4fbe28c531d27605904c476c..9b2cf7afc246f9472eb46ae301c5401271cf693c 100644 (file)
@@ -246,6 +246,9 @@ static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host)
 
        mrq = host->mrq;
 
+       if (host->cmd->error)
+               meson_mx_mmc_soft_reset(host);
+
        host->mrq = NULL;
        host->cmd = NULL;
 
@@ -561,7 +564,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
        mmc->f_max = clk_round_rate(host->cfg_div_clk,
                                    clk_get_rate(host->parent_clk));
 
-       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
+       mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
        mmc->ops = &meson_mx_mmc_ops;
 
        ret = mmc_of_parse(mmc);
index b90b2c97b6cfbe97cf72de97f00d8e49abbdc8a2..a5e05ed0fda3ebca5369ee35544beda9b9dafdc9 100644 (file)
 #define HSQ_NUM_SLOTS  64
 #define HSQ_INVALID_TAG        HSQ_NUM_SLOTS
 
+static void mmc_hsq_retry_handler(struct work_struct *work)
+{
+       struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work);
+       struct mmc_host *mmc = hsq->mmc;
+
+       mmc->ops->request(mmc, hsq->mrq);
+}
+
 static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
 {
        struct mmc_host *mmc = hsq->mmc;
        struct hsq_slot *slot;
        unsigned long flags;
+       int ret = 0;
 
        spin_lock_irqsave(&hsq->lock, flags);
 
@@ -42,7 +51,24 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
 
        spin_unlock_irqrestore(&hsq->lock, flags);
 
-       mmc->ops->request(mmc, hsq->mrq);
+       if (mmc->ops->request_atomic)
+               ret = mmc->ops->request_atomic(mmc, hsq->mrq);
+       else
+               mmc->ops->request(mmc, hsq->mrq);
+
+       /*
+        * If returning BUSY from request_atomic(), which means the card
+        * may be busy now, and we should change to non-atomic context to
+        * try again for this unusual case, to avoid time-consuming operations
+        * in the atomic context.
+        *
+        * Note: we just give a warning for other error cases, since the host
+        * driver will handle them.
+        */
+       if (ret == -EBUSY)
+               schedule_work(&hsq->retry_work);
+       else
+               WARN_ON_ONCE(ret);
 }
 
 static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
@@ -325,6 +351,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
        hsq->mmc->cqe_private = hsq;
        mmc->cqe_ops = &mmc_hsq_ops;
 
+       INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
        spin_lock_init(&hsq->lock);
        init_waitqueue_head(&hsq->wait_queue);
 
index 18b9cf55925f7f4eae25c8122a15ac9574640208..ffdd9cd172c349b336207da2ef96e2c21ad4168c 100644 (file)
@@ -12,6 +12,7 @@ struct mmc_hsq {
        wait_queue_head_t wait_queue;
        struct hsq_slot *slot;
        spinlock_t lock;
+       struct work_struct retry_work;
 
        int next_tag;
        int num_slots;
index 951f76dc1ddd9277869394e6802f7c51276526c8..39bb1e30c2d7223602f9d5145f36a6fce1519f6c 100644 (file)
 
 #define MMC_SPI_BLOCKSIZE      512
 
-
-/* These fixed timeouts come from the latest SD specs, which say to ignore
- * the CSD values.  The R1B value is for card erase (e.g. the "I forgot the
- * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
- * reads which takes nowhere near that long.  Older cards may be able to use
- * shorter timeouts ... but why bother?
- */
-#define r1b_timeout            (HZ * 3)
+#define MMC_SPI_R1B_TIMEOUT_MS 3000
+#define MMC_SPI_INIT_TIMEOUT_MS        3000
 
 /* One of the critical speed parameters is the amount of data which may
  * be transferred in one command. If this value is too low, the SD card
@@ -248,6 +242,7 @@ static char *maptype(struct mmc_command *cmd)
 static int mmc_spi_response_get(struct mmc_spi_host *host,
                struct mmc_command *cmd, int cs_on)
 {
+       unsigned long timeout_ms;
        u8      *cp = host->data->status;
        u8      *end = cp + host->t.len;
        int     value = 0;
@@ -346,8 +341,11 @@ checkstatus:
                /* maybe we read all the busy tokens already */
                while (cp < end && *cp == 0)
                        cp++;
-               if (cp == end)
-                       mmc_spi_wait_unbusy(host, r1b_timeout);
+               if (cp == end) {
+                       timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
+                               MMC_SPI_R1B_TIMEOUT_MS;
+                       mmc_spi_wait_unbusy(host, msecs_to_jiffies(timeout_ms));
+               }
                break;
 
        /* SPI R2 == R1 + second status byte; SEND_STATUS
@@ -1118,7 +1116,7 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host)
        /* Try to be very sure any previous command has completed;
         * wait till not-busy, skip debris from any old commands.
         */
-       mmc_spi_wait_unbusy(host, r1b_timeout);
+       mmc_spi_wait_unbusy(host, msecs_to_jiffies(MMC_SPI_INIT_TIMEOUT_MS));
        mmc_spi_readbytes(host, 10);
 
        /*
index 647567def6125789947c1aa432c82ba7019ac7d2..a69d6a0c2e157dfa253b80a51e1b237731dc59c5 100644 (file)
@@ -1861,31 +1861,17 @@ static int mmci_get_cd(struct mmc_host *mmc)
 static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct mmci_host *host = mmc_priv(mmc);
-       int ret = 0;
-
-       if (!IS_ERR(mmc->supply.vqmmc)) {
+       int ret;
 
-               switch (ios->signal_voltage) {
-               case MMC_SIGNAL_VOLTAGE_330:
-                       ret = regulator_set_voltage(mmc->supply.vqmmc,
-                                               2700000, 3600000);
-                       break;
-               case MMC_SIGNAL_VOLTAGE_180:
-                       ret = regulator_set_voltage(mmc->supply.vqmmc,
-                                               1700000, 1950000);
-                       break;
-               case MMC_SIGNAL_VOLTAGE_120:
-                       ret = regulator_set_voltage(mmc->supply.vqmmc,
-                                               1100000, 1300000);
-                       break;
-               }
+       ret = mmc_regulator_set_vqmmc(mmc, ios);
 
-               if (!ret && host->ops && host->ops->post_sig_volt_switch)
-                       ret = host->ops->post_sig_volt_switch(host, ios);
+       if (!ret && host->ops && host->ops->post_sig_volt_switch)
+               ret = host->ops->post_sig_volt_switch(host, ios);
+       else if (ret)
+               ret = 0;
 
-               if (ret)
-                       dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
-       }
+       if (ret < 0)
+               dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
 
        return ret;
 }
index d33e62bd61530905c1e8c3f899de2739d86bc620..51db30acf4dcaf32e08d3e28a8adb3443d2f44a4 100644 (file)
@@ -119,20 +119,19 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host,
 static int sdmmc_idma_setup(struct mmci_host *host)
 {
        struct sdmmc_idma *idma;
+       struct device *dev = mmc_dev(host->mmc);
 
-       idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL);
+       idma = devm_kzalloc(dev, sizeof(*idma), GFP_KERNEL);
        if (!idma)
                return -ENOMEM;
 
        host->dma_priv = idma;
 
        if (host->variant->dma_lli) {
-               idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc),
-                                                  SDMMC_LLI_BUF_LEN,
+               idma->sg_cpu = dmam_alloc_coherent(dev, SDMMC_LLI_BUF_LEN,
                                                   &idma->sg_dma, GFP_KERNEL);
                if (!idma->sg_cpu) {
-                       dev_err(mmc_dev(host->mmc),
-                               "Failed to alloc IDMA descriptor\n");
+                       dev_err(dev, "Failed to alloc IDMA descriptor\n");
                        return -ENOMEM;
                }
                host->mmc->max_segs = SDMMC_LLI_BUF_LEN /
@@ -143,7 +142,7 @@ static int sdmmc_idma_setup(struct mmci_host *host)
                host->mmc->max_seg_size = host->mmc->max_req_size;
        }
 
-       return 0;
+       return dma_set_max_seg_size(dev, host->mmc->max_seg_size);
 }
 
 static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
@@ -188,6 +187,9 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
 static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data)
 {
        writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR);
+
+       if (!data->host_cookie)
+               sdmmc_idma_unprep_data(host, data, 0);
 }
 
 static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
@@ -519,6 +521,7 @@ void sdmmc_variant_init(struct mmci_host *host)
        struct sdmmc_dlyb *dlyb;
 
        host->ops = &sdmmc_variant_ops;
+       host->pwr_reg = readl_relaxed(host->base + MMCIPOWER);
 
        base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL);
        if (IS_ERR(base_dlyb))
index b221c02cc71f8ffdfbfd57af1c16e5bbe03c78e9..39e7fc54c438f6592d3f0d71fa8e5f6197d347b9 100644 (file)
@@ -1369,7 +1369,7 @@ static void msdc_set_buswidth(struct msdc_host *host, u32 width)
 static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct msdc_host *host = mmc_priv(mmc);
-       int ret = 0;
+       int ret;
 
        if (!IS_ERR(mmc->supply.vqmmc)) {
                if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
@@ -1379,18 +1379,19 @@ static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
                }
 
                ret = mmc_regulator_set_vqmmc(mmc, ios);
-               if (ret) {
+               if (ret < 0) {
                        dev_dbg(host->dev, "Regulator set error %d (%d)\n",
                                ret, ios->signal_voltage);
-               } else {
-                       /* Apply different pinctrl settings for different signal voltage */
-                       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
-                               pinctrl_select_state(host->pinctrl, host->pins_uhs);
-                       else
-                               pinctrl_select_state(host->pinctrl, host->pins_default);
+                       return ret;
                }
+
+               /* Apply different pinctrl settings for different signal voltage */
+               if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+                       pinctrl_select_state(host->pinctrl, host->pins_uhs);
+               else
+                       pinctrl_select_state(host->pinctrl, host->pins_default);
        }
-       return ret;
+       return 0;
 }
 
 static int msdc_card_busy(struct mmc_host *mmc)
@@ -2325,7 +2326,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
        if (mmc->caps & MMC_CAP_SDIO_IRQ)
                mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
 
-       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+       mmc->caps |= MMC_CAP_CMD23;
        /* MMC core transfer sizes tunable parameters */
        mmc->max_segs = MAX_BD_NUM;
        if (host->dev_comp->support_64g)
index 203b617126014aaa5062e69636045c4b37fc6a36..cc0752a9df6d4c778ff5cba1583d0f01fa9cb1b7 100644 (file)
@@ -752,8 +752,6 @@ static int mvsd_probe(struct platform_device *pdev)
        if (maxfreq)
                mmc->f_max = maxfreq;
 
-       mmc->caps |= MMC_CAP_ERASE;
-
        spin_lock_init(&host->lock);
 
        host->base = devm_platform_ioremap_resource(pdev, 0);
index d82674aed4474bc6990d27db4b0d8d9b5236beeb..b1820def36c014fe9217da2ea2f707a9e8ae2a22 100644 (file)
@@ -634,8 +634,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
        /* set mmc core parameters */
        mmc->ops = &mxs_mmc_ops;
        mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
-                   MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 |
-                   MMC_CAP_ERASE;
+                   MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
 
        host->broken_cd = of_property_read_bool(np, "broken-cd");
 
index d74e73c95fdffa6acf589ea0483d374a7530561d..33d7af7c7762a8499605790b8ebb6966702e5d35 100644 (file)
@@ -1244,7 +1244,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
 
        mmc->caps = 0;
        if (host->pdata->slots[id].wires >= 4)
-               mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
+               mmc->caps |= MMC_CAP_4_BIT_DATA;
 
        mmc->ops = &mmc_omap_ops;
        mmc->f_min = 400000;
index a379c45b985cebea031d2c33fb72210741813ecc..37b8740513f5fa3586738bdd1003042d5b2d61cc 100644 (file)
@@ -1922,7 +1922,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
 
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
-                    MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23;
+                    MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_CMD23;
 
        mmc->caps |= mmc_pdata(host)->caps;
        if (mmc->caps & MMC_CAP_8_BIT_DATA)
index 01ffe51f413df8556a1731ab02ec3a9ce1721007..5e20c099fe03a8d538dd3305fecbfd1136fc42cd 100644 (file)
@@ -92,6 +92,8 @@
 #define OWL_SD_STATE_RC16ER            BIT(1)
 #define OWL_SD_STATE_CRC7ER            BIT(0)
 
+#define OWL_CMD_TIMEOUT_MS             30000
+
 struct owl_mmc_host {
        struct device *dev;
        struct reset_control *reset;
@@ -172,6 +174,7 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
                             struct mmc_command *cmd,
                             struct mmc_data *data)
 {
+       unsigned long timeout;
        u32 mode, state, resp[2];
        u32 cmd_rsp_mask = 0;
 
@@ -239,7 +242,10 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
        if (data)
                return;
 
-       if (!wait_for_completion_timeout(&owl_host->sdc_complete, 30 * HZ)) {
+       timeout = msecs_to_jiffies(cmd->busy_timeout ? cmd->busy_timeout :
+               OWL_CMD_TIMEOUT_MS);
+
+       if (!wait_for_completion_timeout(&owl_host->sdc_complete, timeout)) {
                dev_err(owl_host->dev, "CMD interrupt timeout\n");
                cmd->error = -ETIMEDOUT;
                return;
index 2a4c83a5f32ef8e2dc3eebca86e1fc8a5defd7f6..14c64caefc64ccab7f017d8ac330150633b41f64 100644 (file)
@@ -36,6 +36,7 @@ struct renesas_sdhi_of_data {
 struct renesas_sdhi_quirks {
        bool hs400_disabled;
        bool hs400_4taps;
+       u32 hs400_bad_taps;
 };
 
 struct tmio_mmc_dma {
@@ -61,8 +62,10 @@ struct renesas_sdhi {
 
        /* Tuning values: 1 for success, 0 for failure */
        DECLARE_BITMAP(taps, BITS_PER_LONG);
+       /* Sampling data comparison: 1 for match, 0 for mismatch */
+       DECLARE_BITMAP(smpcmp, BITS_PER_LONG);
        unsigned int tap_num;
-       unsigned long tap_set;
+       unsigned int tap_set;
 };
 
 #define host_to_priv(host) \
index df826661366f083bb71ff591090fe2f429293901..15e21894bd44a84ffacd77d1c416e552ed0b99ec 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_domain.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/slot-gpio.h>
 #include <linux/mfd/tmio.h>
@@ -82,16 +83,11 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
 {
        struct mmc_host *mmc = host->mmc;
        struct renesas_sdhi *priv = host_to_priv(host);
-       int ret = clk_prepare_enable(priv->clk);
-
-       if (ret < 0)
-               return ret;
+       int ret;
 
        ret = clk_prepare_enable(priv->clk_cd);
-       if (ret < 0) {
-               clk_disable_unprepare(priv->clk);
+       if (ret < 0)
                return ret;
-       }
 
        /*
         * The clock driver may not know what maximum frequency
@@ -197,7 +193,6 @@ static void renesas_sdhi_clk_disable(struct tmio_mmc_host *host)
 {
        struct renesas_sdhi *priv = host_to_priv(host);
 
-       clk_disable_unprepare(priv->clk);
        clk_disable_unprepare(priv->clk_cd);
 }
 
@@ -237,7 +232,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
                        MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
 
        ret = mmc_regulator_set_vqmmc(host->mmc, ios);
-       if (ret)
+       if (ret < 0)
                return ret;
 
        return pinctrl_select_state(priv->pinctrl, pin_state);
@@ -325,6 +320,8 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
 {
        struct tmio_mmc_host *host = mmc_priv(mmc);
        struct renesas_sdhi *priv = host_to_priv(host);
+       u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
+       bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
 
        sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
                sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
@@ -352,10 +349,23 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
                       SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
                       0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
 
+       /* Avoid bad TAP */
+       if (bad_taps & BIT(priv->tap_set)) {
+               u32 new_tap = (priv->tap_set + 1) % priv->tap_num;
+
+               if (bad_taps & BIT(new_tap))
+                       new_tap = (priv->tap_set - 1) % priv->tap_num;
 
-       if (priv->quirks && priv->quirks->hs400_4taps)
-               sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
-                              priv->tap_set / 2);
+               if (bad_taps & BIT(new_tap)) {
+                       new_tap = priv->tap_set;
+                       dev_dbg(&host->pdev->dev, "Can't handle three bad tap in a row\n");
+               }
+
+               priv->tap_set = new_tap;
+       }
+
+       sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
+                      priv->tap_set / (use_4tap ? 2 : 1));
 
        sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
                       SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
@@ -422,20 +432,16 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io
        return 0;
 }
 
-#define SH_MOBILE_SDHI_MAX_TAP 3
+#define SH_MOBILE_SDHI_MIN_TAP_ROW 3
 
 static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
 {
        struct renesas_sdhi *priv = host_to_priv(host);
-       unsigned long tap_cnt;  /* counter of tuning success */
-       unsigned long tap_start;/* start position of tuning success */
-       unsigned long tap_end;  /* end position of tuning success */
-       unsigned long ntap;     /* temporary counter of tuning success */
-       unsigned long i;
+       unsigned int tap_start = 0, tap_end = 0, tap_cnt = 0, rs, re, i;
+       unsigned int taps_size = priv->tap_num * 2, min_tap_row;
+       unsigned long *bitmap;
 
        priv->doing_tune = false;
-
-       /* Clear SCC_RVSREQ */
        sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
 
        /*
@@ -443,42 +449,42 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
         * result requiring the tap to be good in both runs before
         * considering it for tuning selection.
         */
-       for (i = 0; i < priv->tap_num * 2; i++) {
+       for (i = 0; i < taps_size; i++) {
                int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1);
 
                if (!test_bit(i, priv->taps))
                        clear_bit(i + offset, priv->taps);
+
+               if (!test_bit(i, priv->smpcmp))
+                       clear_bit(i + offset, priv->smpcmp);
        }
 
        /*
-        * Find the longest consecutive run of successful probes.  If that
-        * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
-        * center index as the tap.
+        * If all TAP are OK, the sampling clock position is selected by
+        * identifying the change point of data.
         */
-       tap_cnt = 0;
-       ntap = 0;
-       tap_start = 0;
-       tap_end = 0;
-       for (i = 0; i < priv->tap_num * 2; i++) {
-               if (test_bit(i, priv->taps)) {
-                       ntap++;
-               } else {
-                       if (ntap > tap_cnt) {
-                               tap_start = i - ntap;
-                               tap_end = i - 1;
-                               tap_cnt = ntap;
-                       }
-                       ntap = 0;
-               }
+       if (bitmap_full(priv->taps, taps_size)) {
+               bitmap = priv->smpcmp;
+               min_tap_row = 1;
+       } else {
+               bitmap = priv->taps;
+               min_tap_row = SH_MOBILE_SDHI_MIN_TAP_ROW;
        }
 
-       if (ntap > tap_cnt) {
-               tap_start = i - ntap;
-               tap_end = i - 1;
-               tap_cnt = ntap;
+       /*
+        * Find the longest consecutive run of successful probes. If that
+        * is at least SH_MOBILE_SDHI_MIN_TAP_ROW probes long then use the
+        * center index as the tap, otherwise bail out.
+        */
+       bitmap_for_each_set_region(bitmap, rs, re, 0, taps_size) {
+               if (re - rs > tap_cnt) {
+                       tap_end = re;
+                       tap_start = rs;
+                       tap_cnt = tap_end - tap_start;
+               }
        }
 
-       if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
+       if (tap_cnt >= min_tap_row)
                priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num;
        else
                return -EIO;
@@ -511,6 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
 
        priv->doing_tune = true;
        bitmap_zero(priv->taps, priv->tap_num * 2);
+       bitmap_zero(priv->smpcmp, priv->tap_num * 2);
 
        /* Issue CMD19 twice for each tap */
        for (i = 0; i < 2 * priv->tap_num; i++) {
@@ -519,6 +526,9 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
 
                if (mmc_send_tuning(host->mmc, opcode, NULL) == 0)
                        set_bit(i, priv->taps);
+
+               if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
+                       set_bit(i, priv->smpcmp);
        }
 
        return renesas_sdhi_select_tuning(host);
@@ -527,7 +537,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
 static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
 {
        struct renesas_sdhi *priv = host_to_priv(host);
-       unsigned long new_tap = priv->tap_set;
+       unsigned int new_tap = priv->tap_set, error_tap = priv->tap_set;
        u32 val;
 
        val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ);
@@ -539,20 +549,32 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
        /* Change TAP position according to correction status */
        if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
            host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
+               u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
                /*
                 * With HS400, the DAT signal is based on DS, not CLK.
                 * Therefore, use only CMD status.
                 */
                u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) &
                                           SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR;
-               if (!smpcmp)
+               if (!smpcmp) {
                        return false;   /* no error in CMD signal */
-               else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP)
+               } else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) {
                        new_tap++;
-               else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN)
+                       error_tap--;
+               } else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) {
                        new_tap--;
-               else
+                       error_tap++;
+               } else {
                        return true;    /* need retune */
+               }
+
+               /*
+                * When new_tap is a bad tap, we cannot change. Then, we compare
+                * with the HS200 tuning result. When smpcmp[error_tap] is OK,
+                * we can at least retune.
+                */
+               if (bad_taps & BIT(new_tap % priv->tap_num))
+                       return test_bit(error_tap % priv->tap_num, priv->smpcmp);
        } else {
                if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
                        return true;    /* need retune */
@@ -705,17 +727,35 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400 = {
 
 static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
        .hs400_4taps = true,
+       .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
 };
 
 static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
        .hs400_disabled = true,
 };
 
+static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = {
+       .hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7),
+};
+
+static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
+       .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
+};
+
+/*
+ * Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
+ * So, we want to treat them equally and only have a match for ES1.2 to enforce
+ * this if there ever will be a way to distinguish ES1.2.
+ */
 static const struct soc_device_attribute sdhi_quirks_match[]  = {
        { .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
        { .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 },
        { .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
+       { .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
        { .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
+       { .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap },
+       { .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
+       { .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 },
        { .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
        { /* Sentinel. */ },
 };
@@ -860,6 +900,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
        /* All SDHI have SDIO status bits which must be 1 */
        mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
 
+       dev_pm_domain_start(&pdev->dev);
+
        ret = renesas_sdhi_clk_enable(host);
        if (ret)
                goto efree;
@@ -933,10 +975,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
                        goto eirq;
        }
 
-       dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
-                mmc_hostname(host->mmc), (unsigned long)
-                (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
-                host->mmc->f_max / 1000000);
+       dev_info(&pdev->dev, "%s base at %pa, max clock rate %u MHz\n",
+                mmc_hostname(host->mmc), &res->start, host->mmc->f_max / 1000000);
 
        return ret;
 
index 11087976ab19cf296053fc35205bfb3e69cece2a..5a71f6678fd3acccb952e79b26a7718113becba5 100644 (file)
@@ -1347,7 +1347,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
        mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
                MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
-               MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
+               MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
        mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
        mmc->max_current_330 = 400;
        mmc->max_current_180 = 800;
index 81d0dfe553a824b4bcee77985920d48f5947dbf1..a7084c50ad65ff2d6ba24bc31e6da60000c5908c 100644 (file)
@@ -1314,7 +1314,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
        mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
                MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
                MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
-               MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
+               MMC_CAP_SYNC_RUNTIME_PM;
        mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
                MMC_CAP2_NO_SDIO;
 
index 1e616ae56b132a74d50b3003c9bbab7a5ba6b600..444b2769ae2ca3b8ecd8ac96987e2cd6512b987b 100644 (file)
@@ -958,13 +958,6 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
 {
        u32 dcon, imsk, stoptries = 3;
 
-       /* write DCON register */
-
-       if (!data) {
-               writel(0, host->base + S3C2410_SDIDCON);
-               return 0;
-       }
-
        if ((data->blksz & 3) != 0) {
                /* We cannot deal with unaligned blocks with more than
                 * one block being transferred. */
index 6da6d4fb5edd79f7eba922b3d2b4bb350b54cbec..4a6c9ba82538109a559006dda7f65614ad96d1dd 100644 (file)
@@ -97,6 +97,11 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
        u32 tmp;
        int ret;
 
+       ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
+                                0, 10);
+       if (ret)
+               return ret;
+
        tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
              FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
        writel(tmp, reg);
@@ -111,7 +116,10 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
        tmp &= ~SDHCI_CDNS_HRS04_WR;
        writel(tmp, reg);
 
-       return 0;
+       ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
+                                0, 10);
+
+       return ret;
 }
 
 static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
index 5ec8e4bf1ac71eb2b956de84fda2f8d0f3238a1c..1d7f84b23a22d9817fefe5010ee15e23d99ee4df 100644 (file)
@@ -8,6 +8,7 @@
  *   Author: Wolfram Sang <kernel@pengutronix.de>
  */
 
+#include <linux/bitfield.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/delay.h>
@@ -89,7 +90,8 @@
 #define ESDHC_STD_TUNING_EN            (1 << 24)
 /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
 #define ESDHC_TUNING_START_TAP_DEFAULT 0x1
-#define ESDHC_TUNING_START_TAP_MASK    0xff
+#define ESDHC_TUNING_START_TAP_MASK    0x7f
+#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE     (1 << 7)
 #define ESDHC_TUNING_STEP_MASK         0x00070000
 #define ESDHC_TUNING_STEP_SHIFT                16
 
@@ -214,6 +216,7 @@ static const struct esdhc_soc_data usdhc_imx6sl_data = {
 static const struct esdhc_soc_data usdhc_imx6sll_data = {
        .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
                        | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
+                       | ESDHC_FLAG_HS400
                        | ESDHC_FLAG_STATE_LOST_IN_LPMODE,
 };
 
@@ -399,7 +402,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
                                val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
                                        | SDHCI_SUPPORT_SDR50
                                        | SDHCI_USE_SDR50_TUNING
-                                       | (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
+                                       | FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
+                                                    SDHCI_TUNING_MODE_3);
 
                        if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
                                val |= SDHCI_SUPPORT_HS400;
@@ -417,9 +421,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
 
        if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
                val = 0;
-               val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
-               val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
-               val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
+               val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
+               val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
+               val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
        }
 
        if (unlikely(reg == SDHCI_INT_STATUS)) {
@@ -1313,6 +1317,18 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
                                tmp |= imx_data->boarddata.tuning_step
                                        << ESDHC_TUNING_STEP_SHIFT;
                        }
+
+                       /* Disable the CMD CRC check for tuning, if not, need to
+                        * add some delay after every tuning command, because
+                        * hardware standard tuning logic will directly go to next
+                        * step once it detect the CMD CRC error, will not wait for
+                        * the card side to finally send out the tuning data, trigger
+                        * the buffer read ready interrupt immediately. If usdhc send
+                        * the next tuning command some eMMC card will stuck, can't
+                        * response, block the tuning procedure or the first command
+                        * after the whole tuning procedure always can't get any response.
+                        */
+                        tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
                        writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
                } else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
                        /*
@@ -1596,6 +1612,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        if (esdhc_is_usdhc(imx_data)) {
                host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
                host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
+
+               /* GPIO CD can be set as a wakeup source */
+               host->mmc->caps |= MMC_CAP_CD_WAKE;
+
                if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
                        host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
 
@@ -1653,8 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        if (err)
                goto disable_ahb_clk;
 
-       host->tuning_delay = 1;
-
        sdhci_esdhc_imx_hwinit(host);
 
        err = sdhci_add_host(host);
@@ -1731,8 +1749,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
                mmc_retune_needed(host->mmc);
 
        ret = sdhci_suspend_host(host);
-       if (!ret)
-               return pinctrl_pm_select_sleep_state(dev);
+       if (ret)
+               return ret;
+
+       ret = pinctrl_pm_select_sleep_state(dev);
+       if (ret)
+               return ret;
+
+       ret = mmc_gpio_set_cd_wake(host->mmc, true);
 
        return ret;
 }
@@ -1756,6 +1780,9 @@ static int sdhci_esdhc_resume(struct device *dev)
        if (host->mmc->caps2 & MMC_CAP2_CQE)
                ret = cqhci_resume(host->mmc);
 
+       if (!ret)
+               ret = mmc_gpio_set_cd_wake(host->mmc, false);
+
        return ret;
 }
 #endif
diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c
new file mode 100644 (file)
index 0000000..71bf086
--- /dev/null
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale eSDHC ColdFire family controller driver, platform bus.
+ *
+ * Copyright (c) 2020 Timesys Corporation
+ *   Author: Angelo Dureghello <angelo.dureghello@timesys.it>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_data/mmc-esdhc-mcf.h>
+#include <linux/mmc/mmc.h>
+#include "sdhci-pltfm.h"
+#include "sdhci-esdhc.h"
+
+#define        ESDHC_PROCTL_D3CD               0x08
+#define ESDHC_SYS_CTRL_DTOCV_MASK      0x0f
+#define ESDHC_DEFAULT_HOST_CONTROL     0x28
+
+/*
+ * Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25.
+ */
+#define ESDHC_INT_VENDOR_SPEC_DMA_ERR  BIT(28)
+
+struct pltfm_mcf_data {
+       struct clk *clk_ipg;
+       struct clk *clk_ahb;
+       struct clk *clk_per;
+       int aside;
+       int current_bus_width;
+};
+
+static inline void esdhc_mcf_buffer_swap32(u32 *buf, int len)
+{
+       int i;
+       u32 temp;
+
+       len = (len + 3) >> 2;
+
+       for (i = 0; i < len;  i++) {
+               temp = swab32(*buf);
+               *buf++ = temp;
+       }
+}
+
+static inline void esdhc_clrset_be(struct sdhci_host *host,
+                                  u32 mask, u32 val, int reg)
+{
+       void __iomem *base = host->ioaddr + (reg & ~3);
+       u8 shift = (reg & 3) << 3;
+
+       mask <<= shift;
+       val <<= shift;
+
+       if (reg == SDHCI_HOST_CONTROL)
+               val |= ESDHC_PROCTL_D3CD;
+
+       writel((readl(base) & ~mask) | val, base);
+}
+
+/*
+ * Note: mcf is big-endian, single bytes need to be accessed at big endian
+ * offsets.
+ */
+static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
+{
+       void __iomem *base = host->ioaddr + (reg & ~3);
+       u8 shift = (reg & 3) << 3;
+       u32 mask = ~(0xff << shift);
+
+       if (reg == SDHCI_HOST_CONTROL) {
+               u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL;
+               u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3;
+               u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1);
+
+               tmp &= ~0x03;
+               tmp |= dma_bits;
+
+               /*
+                * Recomposition needed, restore always endianness and
+                * keep D3CD and AI, just setting bus width.
+                */
+               host_ctrl |= val;
+               host_ctrl |= (dma_bits << 8);
+               writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+               return;
+       }
+
+       writel((readl(base) & mask) | (val << shift), base);
+}
+
+static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+       void __iomem *base = host->ioaddr + (reg & ~3);
+       u8 shift = (reg & 3) << 3;
+       u32 mask = ~(0xffff << shift);
+
+       switch (reg) {
+       case SDHCI_TRANSFER_MODE:
+               mcf_data->aside = val;
+               return;
+       case SDHCI_COMMAND:
+               if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
+                       val |= SDHCI_CMD_ABORTCMD;
+
+               /*
+                * As for the fsl driver,
+                * we have to set the mode in a single write here.
+                */
+               writel(val << 16 | mcf_data->aside,
+                      host->ioaddr + SDHCI_TRANSFER_MODE);
+               return;
+       }
+
+       writel((readl(base) & mask) | (val << shift), base);
+}
+
+static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg)
+{
+       writel(val, host->ioaddr + reg);
+}
+
+static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
+{
+       if (reg == SDHCI_HOST_CONTROL) {
+               u8 __iomem *base = host->ioaddr + (reg & ~3);
+               u16 val = readw(base + 2);
+               u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK;
+               u8 host_ctrl = val & 0xff;
+
+               host_ctrl &= ~SDHCI_CTRL_DMA_MASK;
+               host_ctrl |= dma_bits;
+
+               return host_ctrl;
+       }
+
+       return readb(host->ioaddr + (reg ^ 0x3));
+}
+
+static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
+{
+       /*
+        * For SDHCI_HOST_VERSION, sdhci specs defines 0xFE,
+        * a wrong offset for us, we are at 0xFC.
+        */
+       if (reg == SDHCI_HOST_VERSION)
+               reg -= 2;
+
+       return readw(host->ioaddr + (reg ^ 0x2));
+}
+
+static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg)
+{
+       u32 val;
+
+       val = readl(host->ioaddr + reg);
+
+       /*
+        * RM (25.3.9) sd pin clock must never exceed 25Mhz.
+        * So forcing legacy mode at 25Mhz.
+        */
+       if (unlikely(reg == SDHCI_CAPABILITIES))
+               val &= ~SDHCI_CAN_DO_HISPD;
+
+       if (unlikely(reg == SDHCI_INT_STATUS)) {
+               if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
+                       val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
+                       val |= SDHCI_INT_ADMA_ERROR;
+               }
+       }
+
+       return val;
+}
+
+static unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host)
+{
+       return 1 << 27;
+}
+
+static void esdhc_mcf_set_timeout(struct sdhci_host *host,
+                                 struct mmc_command *cmd)
+{
+       /* Use maximum timeout counter */
+       esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE,
+                       SDHCI_TIMEOUT_CONTROL);
+}
+
+static void esdhc_mcf_reset(struct sdhci_host *host, u8 mask)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+       sdhci_reset(host, mask);
+
+       esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
+                       mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
+
+       sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+       return pltfm_host->clock;
+}
+
+static unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+       return pltfm_host->clock / 256 / 16;
+}
+
+static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host,
+                                     unsigned int clock)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR;
+       u32 fvco, fsys, fesdhc, temp;
+       const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256};
+       int delta, old_delta = clock;
+       int i, q, ri, rq;
+
+       if (clock == 0) {
+               host->mmc->actual_clock = 0;
+               return;
+       }
+
+       /*
+        * ColdFire eSDHC clock.s
+        *
+        * pll -+-> / outdiv1 --> fsys
+        *      +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS
+        *
+        * mcf5441x datasheet says:
+        * (8.1.2) eSDHC should be 40 MHz max
+        * (25.3.9) eSDHC input is, as example, 96 Mhz ...
+        * (25.3.9) sd pin clock must never exceed 25Mhz
+        *
+        * fvco = fsys * outdvi1 + 1
+        * fshdc = fvco / outdiv3 + 1
+        */
+       temp = readl(pll_dr);
+       fsys = pltfm_host->clock;
+       fvco = fsys * ((temp & 0x1f) + 1);
+       fesdhc = fvco / (((temp >> 10) & 0x1f) + 1);
+
+       for (i = 0; i < 8; ++i) {
+               int result = fesdhc / sdclkfs[i];
+
+               for (q = 1; q < 17; ++q) {
+                       int finale = result / q;
+
+                       delta = abs(clock - finale);
+
+                       if (delta < old_delta) {
+                               old_delta = delta;
+                               ri = i;
+                               rq = q;
+                       }
+               }
+       }
+
+       /*
+        * Apply divisors and re-enable all the clocks
+        */
+       temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) |
+               (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN);
+       esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL);
+
+       host->mmc->actual_clock = clock;
+
+       mdelay(1);
+}
+
+static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+       switch (width) {
+       case MMC_BUS_WIDTH_4:
+               mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS;
+               break;
+       default:
+               mcf_data->current_bus_width = 0;
+               break;
+       }
+
+       esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
+                       mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
+}
+
+static void esdhc_mcf_request_done(struct sdhci_host *host,
+                                  struct mmc_request *mrq)
+{
+       struct scatterlist *sg;
+       u32 *buffer;
+       int i;
+
+       if (!mrq->data || !mrq->data->bytes_xfered)
+               goto exit_done;
+
+       if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE)
+               goto exit_done;
+
+       /*
+        * On mcf5441x there is no hw sdma option/flag to select the dma
+        * transfer endiannes. A swap after the transfer is needed.
+        */
+       for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
+               buffer = (u32 *)sg_virt(sg);
+               esdhc_mcf_buffer_swap32(buffer, sg->length);
+       }
+
+exit_done:
+       mmc_request_done(host->mmc, mrq);
+}
+
+static void esdhc_mcf_copy_to_bounce_buffer(struct sdhci_host *host,
+                                           struct mmc_data *data,
+                                           unsigned int length)
+{
+       sg_copy_to_buffer(data->sg, data->sg_len,
+                         host->bounce_buffer, length);
+
+       esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
+                               data->blksz * data->blocks);
+}
+
+static struct sdhci_ops sdhci_esdhc_ops = {
+       .reset = esdhc_mcf_reset,
+       .set_clock = esdhc_mcf_pltfm_set_clock,
+       .get_max_clock = esdhc_mcf_pltfm_get_max_clock,
+       .get_min_clock = esdhc_mcf_pltfm_get_min_clock,
+       .set_bus_width = esdhc_mcf_pltfm_set_bus_width,
+       .get_max_timeout_count = esdhc_mcf_get_max_timeout_count,
+       .set_timeout = esdhc_mcf_set_timeout,
+       .write_b = esdhc_mcf_writeb_be,
+       .write_w = esdhc_mcf_writew_be,
+       .write_l = esdhc_mcf_writel_be,
+       .read_b = esdhc_mcf_readb_be,
+       .read_w = esdhc_mcf_readw_be,
+       .read_l = esdhc_mcf_readl_be,
+       .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
+       .request_done = esdhc_mcf_request_done,
+};
+
+static const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = {
+       .ops = &sdhci_esdhc_ops,
+       .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA,
+                /*
+                 * Mandatory quirk,
+                 * controller does not support cmd23,
+                 * without, on > 8G cards cmd23 is used, and
+                 * driver times out.
+                 */
+                 SDHCI_QUIRK2_HOST_NO_CMD23,
+};
+
+static int esdhc_mcf_plat_init(struct sdhci_host *host,
+                              struct pltfm_mcf_data *mcf_data)
+{
+       struct mcf_esdhc_platform_data *plat_data;
+
+       if (!host->mmc->parent->platform_data) {
+               dev_err(mmc_dev(host->mmc), "no platform data!\n");
+               return -EINVAL;
+       }
+
+       plat_data = (struct mcf_esdhc_platform_data *)
+                       host->mmc->parent->platform_data;
+
+       /* Card_detect */
+       switch (plat_data->cd_type) {
+       default:
+       case ESDHC_CD_CONTROLLER:
+               /* We have a working card_detect back */
+               host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+               break;
+       case ESDHC_CD_PERMANENT:
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+               break;
+       case ESDHC_CD_NONE:
+               break;
+       }
+
+       switch (plat_data->max_bus_width) {
+       case 4:
+               host->mmc->caps |= MMC_CAP_4_BIT_DATA;
+               break;
+       case 1:
+       default:
+               host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+               break;
+       }
+
+       return 0;
+}
+
+static int sdhci_esdhc_mcf_probe(struct platform_device *pdev)
+{
+       struct sdhci_host *host;
+       struct sdhci_pltfm_host *pltfm_host;
+       struct pltfm_mcf_data *mcf_data;
+       int err;
+
+       host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata,
+                               sizeof(*mcf_data));
+
+       if (IS_ERR(host))
+               return PTR_ERR(host);
+
+       pltfm_host = sdhci_priv(host);
+       mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+       host->sdma_boundary = 0;
+
+       host->flags |= SDHCI_AUTO_CMD12;
+
+       mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+       if (IS_ERR(mcf_data->clk_ipg)) {
+               err = PTR_ERR(mcf_data->clk_ipg);
+               goto err_exit;
+       }
+
+       mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(mcf_data->clk_ahb)) {
+               err = PTR_ERR(mcf_data->clk_ahb);
+               goto err_exit;
+       }
+
+       mcf_data->clk_per = devm_clk_get(&pdev->dev, "per");
+       if (IS_ERR(mcf_data->clk_per)) {
+               err = PTR_ERR(mcf_data->clk_per);
+               goto err_exit;
+       }
+
+       pltfm_host->clk = mcf_data->clk_per;
+       pltfm_host->clock = clk_get_rate(pltfm_host->clk);
+       err = clk_prepare_enable(mcf_data->clk_per);
+       if (err)
+               goto err_exit;
+
+       err = clk_prepare_enable(mcf_data->clk_ipg);
+       if (err)
+               goto unprep_per;
+
+       err = clk_prepare_enable(mcf_data->clk_ahb);
+       if (err)
+               goto unprep_ipg;
+
+       err = esdhc_mcf_plat_init(host, mcf_data);
+       if (err)
+               goto unprep_ahb;
+
+       err = sdhci_setup_host(host);
+       if (err)
+               goto unprep_ahb;
+
+       if (!host->bounce_buffer) {
+               dev_err(&pdev->dev, "bounce buffer not allocated");
+               err = -ENOMEM;
+               goto cleanup;
+       }
+
+       err = __sdhci_add_host(host);
+       if (err)
+               goto cleanup;
+
+       return 0;
+
+cleanup:
+       sdhci_cleanup_host(host);
+unprep_ahb:
+       clk_disable_unprepare(mcf_data->clk_ahb);
+unprep_ipg:
+       clk_disable_unprepare(mcf_data->clk_ipg);
+unprep_per:
+       clk_disable_unprepare(mcf_data->clk_per);
+err_exit:
+       sdhci_pltfm_free(pdev);
+
+       return err;
+}
+
+static int sdhci_esdhc_mcf_remove(struct platform_device *pdev)
+{
+       struct sdhci_host *host = platform_get_drvdata(pdev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+       sdhci_remove_host(host, 0);
+
+       clk_disable_unprepare(mcf_data->clk_ipg);
+       clk_disable_unprepare(mcf_data->clk_ahb);
+       clk_disable_unprepare(mcf_data->clk_per);
+
+       sdhci_pltfm_free(pdev);
+
+       return 0;
+}
+
+static struct platform_driver sdhci_esdhc_mcf_driver = {
+       .driver = {
+               .name = "sdhci-esdhc-mcf",
+       },
+       .probe = sdhci_esdhc_mcf_probe,
+       .remove = sdhci_esdhc_mcf_remove,
+};
+
+module_platform_driver(sdhci_esdhc_mcf_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC");
+MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
+MODULE_LICENSE("GPL v2");
index 947212f16bc6f8ade79cd8ca506611005966ba86..a30796e79b1cbf6d5b13512e24229e6a0af10bcf 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright (c) 2007 Freescale Semiconductor, Inc.
  * Copyright (c) 2009 MontaVista Software, Inc.
  * Copyright (c) 2010 Pengutronix e.K.
- *   Author: Wolfram Sang <w.sang@pengutronix.de>
+ *   Author: Wolfram Sang <kernel@pengutronix.de>
  */
 
 #ifndef _DRIVERS_MMC_SDHCI_ESDHC_H
index a8bcb3f16aa403a316ad705a94ad410ed8b6a682..b277dd7fbdb5d56e7520d36491e3981378b5a42b 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/delay.h>
 #include <linux/mmc/mmc.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_opp.h>
 #include <linux/slab.h>
 #include <linux/iopoll.h>
 #include <linux/regulator/consumer.h>
 #define CORE_FLL_CYCLE_CNT     BIT(18)
 #define CORE_DLL_CLOCK_DISABLE BIT(21)
 
-#define CORE_VENDOR_SPEC_POR_VAL 0xa1c
+#define DLL_USR_CTL_POR_VAL    0x10800
+#define ENABLE_DLL_LOCK_STATUS BIT(26)
+#define FINE_TUNE_MODE_EN      BIT(27)
+#define BIAS_OK_SIGNAL         BIT(29)
+
+#define DLL_CONFIG_3_LOW_FREQ_VAL      0x08
+#define DLL_CONFIG_3_HIGH_FREQ_VAL     0x10
+
+#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
 #define CORE_CLK_PWRSAVE       BIT(1)
 #define CORE_HC_MCLK_SEL_DFLT  (2 << 8)
 #define CORE_HC_MCLK_SEL_HS400 (3 << 8)
 #define CORE_HC_MCLK_SEL_MASK  (3 << 8)
-#define CORE_IO_PAD_PWR_SWITCH_EN      (1 << 15)
-#define CORE_IO_PAD_PWR_SWITCH  (1 << 16)
+#define CORE_IO_PAD_PWR_SWITCH_EN      BIT(15)
+#define CORE_IO_PAD_PWR_SWITCH BIT(16)
 #define CORE_HC_SELECT_IN_EN   BIT(18)
 #define CORE_HC_SELECT_IN_HS400        (6 << 19)
 #define CORE_HC_SELECT_IN_MASK (7 << 19)
 
-#define CORE_3_0V_SUPPORT      (1 << 25)
-#define CORE_1_8V_SUPPORT      (1 << 26)
+#define CORE_3_0V_SUPPORT      BIT(25)
+#define CORE_1_8V_SUPPORT      BIT(26)
 #define CORE_VOLT_SUPPORT      (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
 
 #define CORE_CSR_CDC_CTLR_CFG0         0x130
@@ -156,6 +165,7 @@ struct sdhci_msm_offset {
        u32 core_dll_config_3;
        u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */
        u32 core_ddr_config;
+       u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */
 };
 
 static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
@@ -185,6 +195,7 @@ static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
        .core_dll_config_2 = 0x254,
        .core_dll_config_3 = 0x258,
        .core_ddr_config = 0x25c,
+       .core_dll_usr_ctl = 0x388,
 };
 
 static const struct sdhci_msm_offset sdhci_msm_mci_offset = {
@@ -230,6 +241,7 @@ struct sdhci_msm_variant_ops {
 struct sdhci_msm_variant_info {
        bool mci_removed;
        bool restore_dll_config;
+       bool uses_tassadar_dll;
        const struct sdhci_msm_variant_ops *var_ops;
        const struct sdhci_msm_offset *offset;
 };
@@ -243,6 +255,8 @@ struct sdhci_msm_host {
        struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
        unsigned long clk_rate;
        struct mmc_host *mmc;
+       struct opp_table *opp_table;
+       bool has_opp_table;
        bool use_14lpp_dll_reset;
        bool tuning_done;
        bool calibration_done;
@@ -260,6 +274,9 @@ struct sdhci_msm_host {
        bool use_cdr;
        u32 transfer_mode;
        bool updated_ddr_cfg;
+       bool uses_tassadar_dll;
+       u32 dll_config;
+       u32 ddr_config;
 };
 
 static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
@@ -332,7 +349,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
        int rc;
 
        clock = msm_get_clock_rate_for_bus_mode(host, clock);
-       rc = clk_set_rate(core_clk, clock);
+       rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
        if (rc) {
                pr_err("%s: Failed to set clock at rate %u at timing %d\n",
                       mmc_hostname(host->mmc), clock,
@@ -601,6 +618,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
        config &= ~CORE_CLK_PWRSAVE;
        writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
 
+       config = msm_host->dll_config;
+       writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config);
+
        if (msm_host->use_14lpp_dll_reset) {
                config = readl_relaxed(host->ioaddr +
                                msm_offset->core_dll_config);
@@ -626,7 +646,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
        config |= CORE_DLL_PDN;
        writel_relaxed(config, host->ioaddr +
                        msm_offset->core_dll_config);
-       msm_cm_dll_set_freq(host);
+
+       if (!msm_host->dll_config)
+               msm_cm_dll_set_freq(host);
 
        if (msm_host->use_14lpp_dll_reset &&
            !IS_ERR_OR_NULL(msm_host->xo_clk)) {
@@ -666,7 +688,8 @@ static int msm_init_cm_dll(struct sdhci_host *host)
                        msm_offset->core_dll_config);
 
        if (msm_host->use_14lpp_dll_reset) {
-               msm_cm_dll_set_freq(host);
+               if (!msm_host->dll_config)
+                       msm_cm_dll_set_freq(host);
                config = readl_relaxed(host->ioaddr +
                                msm_offset->core_dll_config_2);
                config &= ~CORE_DLL_CLOCK_DISABLE;
@@ -674,6 +697,27 @@ static int msm_init_cm_dll(struct sdhci_host *host)
                                msm_offset->core_dll_config_2);
        }
 
+       /*
+        * Configure DLL user control register to enable DLL status.
+        * This setting is applicable to SDCC v5.1 onwards only.
+        */
+       if (msm_host->uses_tassadar_dll) {
+               config = DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN |
+                       ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL;
+               writel_relaxed(config, host->ioaddr +
+                               msm_offset->core_dll_usr_ctl);
+
+               config = readl_relaxed(host->ioaddr +
+                               msm_offset->core_dll_config_3);
+               config &= ~0xFF;
+               if (msm_host->clk_rate < 150000000)
+                       config |= DLL_CONFIG_3_LOW_FREQ_VAL;
+               else
+                       config |= DLL_CONFIG_3_HIGH_FREQ_VAL;
+               writel_relaxed(config, host->ioaddr +
+                       msm_offset->core_dll_config_3);
+       }
+
        config = readl_relaxed(host->ioaddr +
                        msm_offset->core_dll_config);
        config |= CORE_DLL_EN;
@@ -951,7 +995,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
                ddr_cfg_offset = msm_offset->core_ddr_config;
        else
                ddr_cfg_offset = msm_offset->core_ddr_config_old;
-       writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + ddr_cfg_offset);
+       writel_relaxed(msm_host->ddr_config, host->ioaddr + ddr_cfg_offset);
 
        if (mmc->ios.enhanced_strobe) {
                config = readl_relaxed(host->ioaddr +
@@ -1129,6 +1173,12 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
        /* Clock-Data-Recovery used to dynamically adjust RX sampling point */
        msm_host->use_cdr = true;
 
+       /*
+        * Clear tuning_done flag before tuning to ensure proper
+        * HS400 settings.
+        */
+       msm_host->tuning_done = 0;
+
        /*
         * For HS400 tuning in HS200 timing requires:
         * - select MCLK/2 in VENDOR_SPEC
@@ -1830,6 +1880,36 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask)
        sdhci_reset(host, mask);
 }
 
+#define DRIVER_NAME "sdhci_msm"
+#define SDHCI_MSM_DUMP(f, x...) \
+       pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
+
+void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+       const struct sdhci_msm_offset *msm_offset = msm_host->offset;
+
+       SDHCI_MSM_DUMP("----------- VENDOR REGISTER DUMP -----------\n");
+
+       SDHCI_MSM_DUMP(
+                       "DLL sts: 0x%08x | DLL cfg:  0x%08x | DLL cfg2: 0x%08x\n",
+               readl_relaxed(host->ioaddr + msm_offset->core_dll_status),
+               readl_relaxed(host->ioaddr + msm_offset->core_dll_config),
+               readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2));
+       SDHCI_MSM_DUMP(
+                       "DLL cfg3: 0x%08x | DLL usr ctl:  0x%08x | DDR cfg: 0x%08x\n",
+               readl_relaxed(host->ioaddr + msm_offset->core_dll_config_3),
+               readl_relaxed(host->ioaddr + msm_offset->core_dll_usr_ctl),
+               readl_relaxed(host->ioaddr + msm_offset->core_ddr_config));
+       SDHCI_MSM_DUMP(
+                       "Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n",
+               readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec),
+               readl_relaxed(host->ioaddr +
+                       msm_offset->core_vendor_spec_func2),
+               readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3));
+}
+
 static const struct sdhci_msm_variant_ops mci_var_ops = {
        .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed,
        .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed,
@@ -1858,10 +1938,18 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
        .offset = &sdhci_msm_v5_offset,
 };
 
+static const struct sdhci_msm_variant_info sm8250_sdhci_var = {
+       .mci_removed = true,
+       .uses_tassadar_dll = true,
+       .var_ops = &v5_var_ops,
+       .offset = &sdhci_msm_v5_offset,
+};
+
 static const struct of_device_id sdhci_msm_dt_match[] = {
        {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
        {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
        {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
+       {.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
        {},
 };
 
@@ -1877,16 +1965,34 @@ static const struct sdhci_ops sdhci_msm_ops = {
        .write_w = sdhci_msm_writew,
        .write_b = sdhci_msm_writeb,
        .irq    = sdhci_msm_cqe_irq,
+       .dump_vendor_regs = sdhci_msm_dump_vendor_regs,
 };
 
 static const struct sdhci_pltfm_data sdhci_msm_pdata = {
        .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
                  SDHCI_QUIRK_SINGLE_POWER_WRITE |
-                 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+                 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+                 SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
+
        .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
        .ops = &sdhci_msm_ops,
 };
 
+static inline void sdhci_msm_get_of_property(struct platform_device *pdev,
+               struct sdhci_host *host)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+
+       if (of_property_read_u32(node, "qcom,ddr-config",
+                               &msm_host->ddr_config))
+               msm_host->ddr_config = DDR_CONFIG_POR_VAL;
+
+       of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config);
+}
+
+
 static int sdhci_msm_probe(struct platform_device *pdev)
 {
        struct sdhci_host *host;
@@ -1925,10 +2031,12 @@ static int sdhci_msm_probe(struct platform_device *pdev)
        msm_host->restore_dll_config = var_info->restore_dll_config;
        msm_host->var_ops = var_info->var_ops;
        msm_host->offset = var_info->offset;
+       msm_host->uses_tassadar_dll = var_info->uses_tassadar_dll;
 
        msm_offset = msm_host->offset;
 
        sdhci_get_of_property(pdev);
+       sdhci_msm_get_of_property(pdev, host);
 
        msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
 
@@ -1962,8 +2070,23 @@ static int sdhci_msm_probe(struct platform_device *pdev)
        }
        msm_host->bulk_clks[0].clk = clk;
 
+       msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
+       if (IS_ERR(msm_host->opp_table)) {
+               ret = PTR_ERR(msm_host->opp_table);
+               goto bus_clk_disable;
+       }
+
+       /* OPP table is optional */
+       ret = dev_pm_opp_of_add_table(&pdev->dev);
+       if (!ret) {
+               msm_host->has_opp_table = true;
+       } else if (ret != -ENODEV) {
+               dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
+               goto opp_cleanup;
+       }
+
        /* Vote for maximum clock rate for maximum performance */
-       ret = clk_set_rate(clk, INT_MAX);
+       ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX);
        if (ret)
                dev_warn(&pdev->dev, "core clock boost failed\n");
 
@@ -1980,7 +2103,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
        ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
                                      msm_host->bulk_clks);
        if (ret)
-               goto bus_clk_disable;
+               goto opp_cleanup;
 
        /*
         * xo clock is needed for FLL feature of cm_dll.
@@ -2117,6 +2240,10 @@ pm_runtime_disable:
 clk_disable:
        clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
                                   msm_host->bulk_clks);
+opp_cleanup:
+       if (msm_host->has_opp_table)
+               dev_pm_opp_of_remove_table(&pdev->dev);
+       dev_pm_opp_put_clkname(msm_host->opp_table);
 bus_clk_disable:
        if (!IS_ERR(msm_host->bus_clk))
                clk_disable_unprepare(msm_host->bus_clk);
@@ -2135,6 +2262,9 @@ static int sdhci_msm_remove(struct platform_device *pdev)
 
        sdhci_remove_host(host, dead);
 
+       if (msm_host->has_opp_table)
+               dev_pm_opp_of_remove_table(&pdev->dev);
+       dev_pm_opp_put_clkname(msm_host->opp_table);
        pm_runtime_get_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
@@ -2153,6 +2283,8 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
        struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 
+       /* Drop the performance vote */
+       dev_pm_opp_set_rate(dev, 0);
        clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
                                   msm_host->bulk_clks);
 
@@ -2175,9 +2307,11 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
         * restore the SDR DLL settings when the clock is ungated.
         */
        if (msm_host->restore_dll_config && msm_host->clk_rate)
-               return sdhci_msm_restore_sdr_dll_config(host);
+               ret = sdhci_msm_restore_sdr_dll_config(host);
 
-       return 0;
+       dev_pm_opp_set_rate(dev, msm_host->clk_rate);
+
+       return ret;
 }
 
 static const struct dev_pm_ops sdhci_msm_pm_ops = {
index d4905c106c0605f2dd191e9d45d48081e5567463..2a4c8a2f3e64b1f0130bfe75970bfb88b7561b6e 100644 (file)
 #include "sdhci-pltfm.h"
 
 #define SDHCI_ARASAN_VENDOR_REGISTER   0x78
+
+#define SDHCI_ARASAN_ITAPDLY_REGISTER  0xF0F8
+#define SDHCI_ARASAN_OTAPDLY_REGISTER  0xF0FC
+
 #define SDHCI_ARASAN_CQE_BASE_ADDR     0x200
 #define VENDOR_ENHANCED_STROBE         BIT(0)
 
 #define PHY_CLK_TOO_SLOW_HZ            400000
 
+#define SDHCI_ITAPDLY_CHGWIN           0x200
+#define SDHCI_ITAPDLY_ENABLE           0x100
+#define SDHCI_OTAPDLY_ENABLE           0x40
+
 /* Default settings for ZynqMP Clock Phases */
 #define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63,  0,   0, 183, 54,  0, 0}
 #define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
 
+#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
+#define VERSAL_OCLK_PHASE {0,  60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
+
 /*
  * On some SoCs the syscon area has a feature where the upper 16-bits of
  * each 32-bit register act as a write mask for the lower 16-bits.  This allows
@@ -62,22 +73,36 @@ struct sdhci_arasan_soc_ctl_field {
 /**
  * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
  *
- * It's up to the licensee of the Arsan IP block to make these available
- * somewhere if needed.  Presumably these will be scattered somewhere that's
- * accessible via the syscon API.
- *
  * @baseclkfreq:       Where to find corecfg_baseclkfreq
  * @clockmultiplier:   Where to find corecfg_clockmultiplier
+ * @support64b:                Where to find SUPPORT64B bit
  * @hiword_update:     If true, use HIWORD_UPDATE to access the syscon
+ *
+ * It's up to the licensee of the Arsan IP block to make these available
+ * somewhere if needed.  Presumably these will be scattered somewhere that's
+ * accessible via the syscon API.
  */
 struct sdhci_arasan_soc_ctl_map {
        struct sdhci_arasan_soc_ctl_field       baseclkfreq;
        struct sdhci_arasan_soc_ctl_field       clockmultiplier;
+       struct sdhci_arasan_soc_ctl_field       support64b;
        bool                                    hiword_update;
 };
 
 /**
- * struct sdhci_arasan_clk_data
+ * struct sdhci_arasan_clk_ops - Clock Operations for Arasan SD controller
+ *
+ * @sdcardclk_ops:     The output clock related operations
+ * @sampleclk_ops:     The sample clock related operations
+ */
+struct sdhci_arasan_clk_ops {
+       const struct clk_ops *sdcardclk_ops;
+       const struct clk_ops *sampleclk_ops;
+};
+
+/**
+ * struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
+ *
  * @sdcardclk_hw:      Struct for the clock we might provide to a PHY.
  * @sdcardclk:         Pointer to normal 'struct clock' for sdcardclk_hw.
  * @sampleclk_hw:      Struct for the clock we might provide to a PHY.
@@ -103,14 +128,18 @@ struct sdhci_arasan_zynqmp_clk_data {
 };
 
 /**
- * struct sdhci_arasan_data
+ * struct sdhci_arasan_data - Arasan Controller Data
+ *
  * @host:              Pointer to the main SDHCI host structure.
  * @clk_ahb:           Pointer to the AHB clock
  * @phy:               Pointer to the generic phy
  * @is_phy_on:         True if the PHY is on; false if not.
+ * @has_cqe:           True if controller has command queuing engine.
  * @clk_data:          Struct for the Arasan Controller Clock Data.
+ * @clk_ops:           Struct for the Arasan Controller Clock Operations.
  * @soc_ctl_base:      Pointer to regmap for syscon for soc_ctl registers.
  * @soc_ctl_map:       Map to get offsets into soc_ctl registers.
+ * @quirks:            Arasan deviations from spec.
  */
 struct sdhci_arasan_data {
        struct sdhci_host *host;
@@ -120,10 +149,11 @@ struct sdhci_arasan_data {
 
        bool            has_cqe;
        struct sdhci_arasan_clk_data clk_data;
+       const struct sdhci_arasan_clk_ops *clk_ops;
 
        struct regmap   *soc_ctl_base;
        const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
-       unsigned int    quirks; /* Arasan deviations from spec */
+       unsigned int    quirks;
 
 /* Controller does not have CD wired and will not function normally without */
 #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST        BIT(0)
@@ -135,6 +165,7 @@ struct sdhci_arasan_data {
 struct sdhci_arasan_of_data {
        const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
        const struct sdhci_pltfm_data *pdata;
+       const struct sdhci_arasan_clk_ops *clk_ops;
 };
 
 static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
@@ -155,17 +186,26 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
        .hiword_update = false,
 };
 
+static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
+       .baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
+       .clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
+       .support64b = { .reg = 0x4, .width = 1, .shift = 24 },
+       .hiword_update = false,
+};
+
 /**
  * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
  *
+ * @host:      The sdhci_host
+ * @fld:       The field to write to
+ * @val:       The value to write
+ *
  * This function allows writing to fields in sdhci_arasan_soc_ctl_map.
  * Note that if a field is specified as not available (shift < 0) then
  * this function will silently return an error code.  It will be noisy
  * and print errors for any other (unexpected) errors.
  *
- * @host:      The sdhci_host
- * @fld:       The field to write to
- * @val:       The value to write
+ * Return: 0 on success and error value on error
  */
 static int sdhci_arasan_syscon_write(struct sdhci_host *host,
                                   const struct sdhci_arasan_soc_ctl_field *fld,
@@ -335,29 +375,6 @@ static const struct sdhci_ops sdhci_arasan_ops = {
        .set_power = sdhci_set_power_and_bus_voltage,
 };
 
-static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
-       .ops = &sdhci_arasan_ops,
-       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
-       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
-                       SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
-                       SDHCI_QUIRK2_STOP_WITH_TC,
-};
-
-static struct sdhci_arasan_of_data sdhci_arasan_data = {
-       .pdata = &sdhci_arasan_pdata,
-};
-
-static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
-       .ops = &sdhci_arasan_ops,
-       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
-                       SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
-                       SDHCI_QUIRK2_STOP_WITH_TC,
-};
-
-static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
-       .pdata = &sdhci_arasan_zynqmp_pdata,
-};
-
 static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
 {
        int cmd_error = 0;
@@ -414,28 +431,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
                        SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
 };
 
-static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
-       .soc_ctl_map = &rk3399_soc_ctl_map,
-       .pdata = &sdhci_arasan_cqe_pdata,
-};
-
-static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
-       .soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
-       .pdata = &sdhci_arasan_cqe_pdata,
-};
-
-static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
-       .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
-       .pdata = &sdhci_arasan_cqe_pdata,
-};
-
 #ifdef CONFIG_PM_SLEEP
 /**
  * sdhci_arasan_suspend - Suspend method for the driver
  * @dev:       Address of the device structure
- * Returns 0 on success and error value on error
  *
  * Put the device in a low power state.
+ *
+ * Return: 0 on success and error value on error
  */
 static int sdhci_arasan_suspend(struct device *dev)
 {
@@ -476,9 +479,10 @@ static int sdhci_arasan_suspend(struct device *dev)
 /**
  * sdhci_arasan_resume - Resume method for the driver
  * @dev:       Address of the device structure
- * Returns 0 on success and error value on error
  *
  * Resume operation after suspend
+ *
+ * Return: 0 on success and error value on error
  */
 static int sdhci_arasan_resume(struct device *dev)
 {
@@ -524,54 +528,19 @@ static int sdhci_arasan_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
                         sdhci_arasan_resume);
 
-static const struct of_device_id sdhci_arasan_of_match[] = {
-       /* SoC-specific compatible strings w/ soc_ctl_map */
-       {
-               .compatible = "rockchip,rk3399-sdhci-5.1",
-               .data = &sdhci_arasan_rk3399_data,
-       },
-       {
-               .compatible = "intel,lgm-sdhci-5.1-emmc",
-               .data = &intel_lgm_emmc_data,
-       },
-       {
-               .compatible = "intel,lgm-sdhci-5.1-sdxc",
-               .data = &intel_lgm_sdxc_data,
-       },
-       /* Generic compatible below here */
-       {
-               .compatible = "arasan,sdhci-8.9a",
-               .data = &sdhci_arasan_data,
-       },
-       {
-               .compatible = "arasan,sdhci-5.1",
-               .data = &sdhci_arasan_data,
-       },
-       {
-               .compatible = "arasan,sdhci-4.9a",
-               .data = &sdhci_arasan_data,
-       },
-       {
-               .compatible = "xlnx,zynqmp-8.9a",
-               .data = &sdhci_arasan_zynqmp_data,
-       },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
-
 /**
  * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
  *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @parent_rate:               The parent rate (should be rate of clk_xin).
+ *
  * Return the current actual rate of the SD card clock.  This can be used
  * to communicate with out PHY.
  *
- * @hw:                        Pointer to the hardware clock structure.
- * @parent_rate                The parent rate (should be rate of clk_xin).
- * Returns the card clock rate.
+ * Return: The card clock rate.
  */
 static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
                                                      unsigned long parent_rate)
-
 {
        struct sdhci_arasan_clk_data *clk_data =
                container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
@@ -589,16 +558,16 @@ static const struct clk_ops arasan_sdcardclk_ops = {
 /**
  * sdhci_arasan_sampleclk_recalc_rate - Return the sampling clock rate
  *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @parent_rate:               The parent rate (should be rate of clk_xin).
+ *
  * Return the current actual rate of the sampling clock.  This can be used
  * to communicate with out PHY.
  *
- * @hw:                        Pointer to the hardware clock structure.
- * @parent_rate                The parent rate (should be rate of clk_xin).
- * Returns the sample clock rate.
+ * Return: The sample clock rate.
  */
 static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw,
                                                      unsigned long parent_rate)
-
 {
        struct sdhci_arasan_clk_data *clk_data =
                container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
@@ -616,14 +585,14 @@ static const struct clk_ops arasan_sampleclk_ops = {
 /**
  * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
  *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
  * Set the SD Output Clock Tap Delays for Output path
  *
- * @hw:                        Pointer to the hardware clock structure.
- * @degrees            The clock phase shift between 0 - 359.
  * Return: 0 on success and error value on error
  */
 static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
-
 {
        struct sdhci_arasan_clk_data *clk_data =
                container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
@@ -688,14 +657,14 @@ static const struct clk_ops zynqmp_sdcardclk_ops = {
 /**
  * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
  *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
  * Set the SD Input Clock Tap Delays for Input path
  *
- * @hw:                        Pointer to the hardware clock structure.
- * @degrees            The clock phase shift between 0 - 359.
  * Return: 0 on success and error value on error
  */
 static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
-
 {
        struct sdhci_arasan_clk_data *clk_data =
                container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
@@ -757,6 +726,152 @@ static const struct clk_ops zynqmp_sampleclk_ops = {
        .set_phase = sdhci_zynqmp_sampleclk_set_phase,
 };
 
+/**
+ * sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
+{
+       struct sdhci_arasan_clk_data *clk_data =
+               container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+       struct sdhci_arasan_data *sdhci_arasan =
+               container_of(clk_data, struct sdhci_arasan_data, clk_data);
+       struct sdhci_host *host = sdhci_arasan->host;
+       u8 tap_delay, tap_max = 0;
+
+       /*
+        * This is applicable for SDHCI_SPEC_300 and above
+        * Versal does not set phase for <=25MHz clock.
+        * If degrees is zero, no need to do anything.
+        */
+       if (host->version < SDHCI_SPEC_300 ||
+           host->timing == MMC_TIMING_LEGACY ||
+           host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
+               return 0;
+
+       switch (host->timing) {
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+       case MMC_TIMING_UHS_SDR25:
+       case MMC_TIMING_UHS_DDR50:
+       case MMC_TIMING_MMC_DDR52:
+               /* For 50MHz clock, 30 Taps are available */
+               tap_max = 30;
+               break;
+       case MMC_TIMING_UHS_SDR50:
+               /* For 100MHz clock, 15 Taps are available */
+               tap_max = 15;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_MMC_HS200:
+               /* For 200MHz clock, 8 Taps are available */
+               tap_max = 8;
+       default:
+               break;
+       }
+
+       tap_delay = (degrees * tap_max) / 360;
+
+       /* Set the Clock Phase */
+       if (tap_delay) {
+               u32 regval;
+
+               regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
+               regval |= SDHCI_OTAPDLY_ENABLE;
+               sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+               regval |= tap_delay;
+               sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
+       }
+
+       return 0;
+}
+
+static const struct clk_ops versal_sdcardclk_ops = {
+       .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
+       .set_phase = sdhci_versal_sdcardclk_set_phase,
+};
+
+/**
+ * sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * @hw:                        Pointer to the hardware clock structure.
+ * @degrees:           The clock phase shift between 0 - 359.
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees)
+{
+       struct sdhci_arasan_clk_data *clk_data =
+               container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
+       struct sdhci_arasan_data *sdhci_arasan =
+               container_of(clk_data, struct sdhci_arasan_data, clk_data);
+       struct sdhci_host *host = sdhci_arasan->host;
+       u8 tap_delay, tap_max = 0;
+
+       /*
+        * This is applicable for SDHCI_SPEC_300 and above
+        * Versal does not set phase for <=25MHz clock.
+        * If degrees is zero, no need to do anything.
+        */
+       if (host->version < SDHCI_SPEC_300 ||
+           host->timing == MMC_TIMING_LEGACY ||
+           host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
+               return 0;
+
+       switch (host->timing) {
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+       case MMC_TIMING_UHS_SDR25:
+       case MMC_TIMING_UHS_DDR50:
+       case MMC_TIMING_MMC_DDR52:
+               /* For 50MHz clock, 120 Taps are available */
+               tap_max = 120;
+               break;
+       case MMC_TIMING_UHS_SDR50:
+               /* For 100MHz clock, 60 Taps are available */
+               tap_max = 60;
+               break;
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_MMC_HS200:
+               /* For 200MHz clock, 30 Taps are available */
+               tap_max = 30;
+       default:
+               break;
+       }
+
+       tap_delay = (degrees * tap_max) / 360;
+
+       /* Set the Clock Phase */
+       if (tap_delay) {
+               u32 regval;
+
+               regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
+               regval |= SDHCI_ITAPDLY_CHGWIN;
+               sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+               regval |= SDHCI_ITAPDLY_ENABLE;
+               sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+               regval |= tap_delay;
+               sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+               regval &= ~SDHCI_ITAPDLY_CHGWIN;
+               sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
+       }
+
+       return 0;
+}
+
+static const struct clk_ops versal_sampleclk_ops = {
+       .recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
+       .set_phase = sdhci_versal_sampleclk_set_phase,
+};
+
 static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
 {
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -804,6 +919,9 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
 /**
  * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
  *
+ * @host:              The sdhci_host
+ * @value:             The value to write
+ *
  * The corecfg_clockmultiplier is supposed to contain clock multiplier
  * value of programmable clock generator.
  *
@@ -815,8 +933,6 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
  * - The value of corecfg_clockmultiplier should sync with that of corresponding
  *   value reading from sdhci_capability_register. So this function is called
  *   once at probe time and never called again.
- *
- * @host:              The sdhci_host
  */
 static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
                                                u32 value)
@@ -843,6 +959,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
 /**
  * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
  *
+ * @host:              The sdhci_host
+ *
  * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin.  This
  * function can be used to make that happen.
  *
@@ -854,8 +972,6 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
  * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
  *   to achieve lower clock rates.  That means that this function is called once
  *   at probe time and never called again.
- *
- * @host:              The sdhci_host
  */
 static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
 {
@@ -919,10 +1035,10 @@ static void arasan_dt_read_clk_phase(struct device *dev,
 /**
  * arasan_dt_parse_clk_phases - Read Clock Delay values from DT
  *
- * Called at initialization to parse the values of Clock Delays.
- *
  * @dev:               Pointer to our struct device.
  * @clk_data:          Pointer to the Clock Data structure
+ *
+ * Called at initialization to parse the values of Clock Delays.
  */
 static void arasan_dt_parse_clk_phases(struct device *dev,
                                       struct sdhci_arasan_clk_data *clk_data)
@@ -954,6 +1070,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
                }
        }
 
+       if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) {
+               iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE;
+               oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE;
+
+               for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
+                       clk_data->clk_phase_in[i] = iclk_phase[i];
+                       clk_data->clk_phase_out[i] = oclk_phase[i];
+               }
+       }
+
        arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
                                 "clk-phase-legacy");
        arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
@@ -978,17 +1104,191 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
                                 "clk-phase-mmc-hs400");
 }
 
+static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
+       .ops = &sdhci_arasan_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                       SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+                       SDHCI_QUIRK2_STOP_WITH_TC,
+};
+
+static const struct sdhci_arasan_clk_ops arasan_clk_ops = {
+       .sdcardclk_ops = &arasan_sdcardclk_ops,
+       .sampleclk_ops = &arasan_sampleclk_ops,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
+       .pdata = &sdhci_arasan_pdata,
+       .clk_ops = &arasan_clk_ops,
+};
+
+static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
+       .ops = &sdhci_arasan_cqe_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+               SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+               SDHCI_QUIRK_NO_LED |
+               SDHCI_QUIRK_32BIT_DMA_ADDR |
+               SDHCI_QUIRK_32BIT_DMA_SIZE |
+               SDHCI_QUIRK_32BIT_ADMA_SIZE,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+               SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+               SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
+               SDHCI_QUIRK2_STOP_WITH_TC |
+               SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
+};
+
+static const struct sdhci_pltfm_data sdhci_keembay_sd_pdata = {
+       .ops = &sdhci_arasan_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+               SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+               SDHCI_QUIRK_NO_LED |
+               SDHCI_QUIRK_32BIT_DMA_ADDR |
+               SDHCI_QUIRK_32BIT_DMA_SIZE |
+               SDHCI_QUIRK_32BIT_ADMA_SIZE,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+               SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+               SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
+               SDHCI_QUIRK2_STOP_WITH_TC |
+               SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
+};
+
+static const struct sdhci_pltfm_data sdhci_keembay_sdio_pdata = {
+       .ops = &sdhci_arasan_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+               SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+               SDHCI_QUIRK_NO_LED |
+               SDHCI_QUIRK_32BIT_DMA_ADDR |
+               SDHCI_QUIRK_32BIT_DMA_SIZE |
+               SDHCI_QUIRK_32BIT_ADMA_SIZE,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+               SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+               SDHCI_QUIRK2_HOST_OFF_CARD_ON |
+               SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
+       .soc_ctl_map = &rk3399_soc_ctl_map,
+       .pdata = &sdhci_arasan_cqe_pdata,
+       .clk_ops = &arasan_clk_ops,
+};
+
+static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
+       .soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
+       .pdata = &sdhci_arasan_cqe_pdata,
+       .clk_ops = &arasan_clk_ops,
+};
+
+static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
+       .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
+       .pdata = &sdhci_arasan_cqe_pdata,
+       .clk_ops = &arasan_clk_ops,
+};
+
+static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
+       .ops = &sdhci_arasan_ops,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                       SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
+                       SDHCI_QUIRK2_STOP_WITH_TC,
+};
+
+static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = {
+       .sdcardclk_ops = &zynqmp_sdcardclk_ops,
+       .sampleclk_ops = &zynqmp_sampleclk_ops,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
+       .pdata = &sdhci_arasan_zynqmp_pdata,
+       .clk_ops = &zynqmp_clk_ops,
+};
+
+static const struct sdhci_arasan_clk_ops versal_clk_ops = {
+       .sdcardclk_ops = &versal_sdcardclk_ops,
+       .sampleclk_ops = &versal_sampleclk_ops,
+};
+
+static struct sdhci_arasan_of_data sdhci_arasan_versal_data = {
+       .pdata = &sdhci_arasan_zynqmp_pdata,
+       .clk_ops = &versal_clk_ops,
+};
+
+static struct sdhci_arasan_of_data intel_keembay_emmc_data = {
+       .soc_ctl_map = &intel_keembay_soc_ctl_map,
+       .pdata = &sdhci_keembay_emmc_pdata,
+};
+
+static struct sdhci_arasan_of_data intel_keembay_sd_data = {
+       .soc_ctl_map = &intel_keembay_soc_ctl_map,
+       .pdata = &sdhci_keembay_sd_pdata,
+};
+
+static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
+       .soc_ctl_map = &intel_keembay_soc_ctl_map,
+       .pdata = &sdhci_keembay_sdio_pdata,
+};
+
+static const struct of_device_id sdhci_arasan_of_match[] = {
+       /* SoC-specific compatible strings w/ soc_ctl_map */
+       {
+               .compatible = "rockchip,rk3399-sdhci-5.1",
+               .data = &sdhci_arasan_rk3399_data,
+       },
+       {
+               .compatible = "intel,lgm-sdhci-5.1-emmc",
+               .data = &intel_lgm_emmc_data,
+       },
+       {
+               .compatible = "intel,lgm-sdhci-5.1-sdxc",
+               .data = &intel_lgm_sdxc_data,
+       },
+       {
+               .compatible = "intel,keembay-sdhci-5.1-emmc",
+               .data = &intel_keembay_emmc_data,
+       },
+       {
+               .compatible = "intel,keembay-sdhci-5.1-sd",
+               .data = &intel_keembay_sd_data,
+       },
+       {
+               .compatible = "intel,keembay-sdhci-5.1-sdio",
+               .data = &intel_keembay_sdio_data,
+       },
+       /* Generic compatible below here */
+       {
+               .compatible = "arasan,sdhci-8.9a",
+               .data = &sdhci_arasan_generic_data,
+       },
+       {
+               .compatible = "arasan,sdhci-5.1",
+               .data = &sdhci_arasan_generic_data,
+       },
+       {
+               .compatible = "arasan,sdhci-4.9a",
+               .data = &sdhci_arasan_generic_data,
+       },
+       {
+               .compatible = "xlnx,zynqmp-8.9a",
+               .data = &sdhci_arasan_zynqmp_data,
+       },
+       {
+               .compatible = "xlnx,versal-8.9a",
+               .data = &sdhci_arasan_versal_data,
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
+
 /**
  * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
  *
+ * @sdhci_arasan:      Our private data structure.
+ * @clk_xin:           Pointer to the functional clock
+ * @dev:               Pointer to our struct device.
+ *
  * Some PHY devices need to know what the actual card clock is.  In order for
  * them to find out, we'll provide a clock through the common clock framework
  * for them to query.
  *
- * @sdhci_arasan:      Our private data structure.
- * @clk_xin:           Pointer to the functional clock
- * @dev:               Pointer to our struct device.
- * Returns 0 on success and error value on error
+ * Return: 0 on success and error value on error
  */
 static int
 sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
@@ -1012,10 +1312,7 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
        sdcardclk_init.parent_names = &parent_clk_name;
        sdcardclk_init.num_parents = 1;
        sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
-       if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
-               sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
-       else
-               sdcardclk_init.ops = &arasan_sdcardclk_ops;
+       sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops;
 
        clk_data->sdcardclk_hw.init = &sdcardclk_init;
        clk_data->sdcardclk =
@@ -1033,14 +1330,15 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
 /**
  * sdhci_arasan_register_sampleclk - Register the sampleclk for a PHY to use
  *
+ * @sdhci_arasan:      Our private data structure.
+ * @clk_xin:           Pointer to the functional clock
+ * @dev:               Pointer to our struct device.
+ *
  * Some PHY devices need to know what the actual card clock is.  In order for
  * them to find out, we'll provide a clock through the common clock framework
  * for them to query.
  *
- * @sdhci_arasan:      Our private data structure.
- * @clk_xin:           Pointer to the functional clock
- * @dev:               Pointer to our struct device.
- * Returns 0 on success and error value on error
+ * Return: 0 on success and error value on error
  */
 static int
 sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
@@ -1064,10 +1362,7 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
        sampleclk_init.parent_names = &parent_clk_name;
        sampleclk_init.num_parents = 1;
        sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
-       if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
-               sampleclk_init.ops = &zynqmp_sampleclk_ops;
-       else
-               sampleclk_init.ops = &arasan_sampleclk_ops;
+       sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops;
 
        clk_data->sampleclk_hw.init = &sampleclk_init;
        clk_data->sampleclk =
@@ -1085,10 +1380,10 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
 /**
  * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
  *
+ * @dev:               Pointer to our struct device.
+ *
  * Should be called any time we're exiting and sdhci_arasan_register_sdclk()
  * returned success.
- *
- * @dev:               Pointer to our struct device.
  */
 static void sdhci_arasan_unregister_sdclk(struct device *dev)
 {
@@ -1100,9 +1395,47 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
        of_clk_del_provider(dev->of_node);
 }
 
+/**
+ * sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
+ *
+ * This should be set based on the System Address Bus.
+ * 0: the Core supports only 32-bit System Address Bus.
+ * 1: the Core supports 64-bit System Address Bus.
+ *
+ * NOTES:
+ * - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
+ *   Keem Bay does not support 64-bit access.
+ *
+ * @host               The sdhci_host
+ */
+static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+       const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
+               sdhci_arasan->soc_ctl_map;
+
+       /* Having a map is optional */
+       if (!soc_ctl_map)
+               return;
+
+       /* If we have a map, we expect to have a syscon */
+       if (!sdhci_arasan->soc_ctl_base) {
+               pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
+                       mmc_hostname(host->mmc));
+               return;
+       }
+
+       sdhci_arasan_syscon_write(host, &soc_ctl_map->support64b, value);
+}
+
 /**
  * sdhci_arasan_register_sdclk - Register the sdcardclk for a PHY to use
  *
+ * @sdhci_arasan:      Our private data structure.
+ * @clk_xin:           Pointer to the functional clock
+ * @dev:               Pointer to our struct device.
+ *
  * Some PHY devices need to know what the actual card clock is.  In order for
  * them to find out, we'll provide a clock through the common clock framework
  * for them to query.
@@ -1115,10 +1448,7 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
  * to create nice clean device tree bindings and later (if needed) we can try
  * re-architecting SDHCI if we see some benefit to it.
  *
- * @sdhci_arasan:      Our private data structure.
- * @clk_xin:           Pointer to the functional clock
- * @dev:               Pointer to our struct device.
- * Returns 0 on success and error value on error
+ * Return: 0 on success and error value on error
  */
 static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
                                       struct clk *clk_xin,
@@ -1215,6 +1545,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
        sdhci_arasan->host = host;
 
        sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
+       sdhci_arasan->clk_ops = data->clk_ops;
 
        node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
        if (node) {
@@ -1270,6 +1601,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
                                    "rockchip,rk3399-sdhci-5.1"))
                sdhci_arasan_update_clockmultiplier(host, 0x0);
 
+       if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
+           of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
+           of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
+               sdhci_arasan_update_clockmultiplier(host, 0x0);
+               sdhci_arasan_update_support64b(host, 0x0);
+
+               host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+       }
+
        sdhci_arasan_update_baseclkfreq(host);
 
        ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
index c79bff5e2280a0dfd3abd28845c795a835e10e50..1ece2c50042c62e421776a2f7b837f6a4cbfbd0d 100644 (file)
@@ -6,6 +6,7 @@
  *              2015 Ludovic Desroches <ludovic.desroches@atmel.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/err.h>
@@ -120,9 +121,12 @@ static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
            || mmc_gpio_get_cd(host->mmc) >= 0)
                sdhci_at91_set_force_card_detect(host);
 
-       if (priv->cal_always_on && (mask & SDHCI_RESET_ALL))
-               sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
+       if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) {
+               u32 calcr = sdhci_readl(host, SDMMC_CALCR);
+
+               sdhci_writel(host, calcr | SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
                             SDMMC_CALCR);
+       }
 }
 
 static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
@@ -179,9 +183,9 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
        clk_mul = gck_rate / clk_base_rate - 1;
 
        caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK;
-       caps0 |= (clk_base << SDHCI_CLOCK_BASE_SHIFT) & SDHCI_CLOCK_V3_BASE_MASK;
+       caps0 |= FIELD_PREP(SDHCI_CLOCK_V3_BASE_MASK, clk_base);
        caps1 &= ~SDHCI_CLOCK_MUL_MASK;
-       caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK;
+       caps1 |= FIELD_PREP(SDHCI_CLOCK_MUL_MASK, clk_mul);
        /* Set capabilities in r/w mode. */
        writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
        writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);
index a5137845a1c7841ab492fcac5cc7ff69d7c9708a..64ac0dbee95c96ecafba141048d4598baf75a247 100644 (file)
@@ -16,6 +16,9 @@
 
 #include "sdhci-pltfm.h"
 
+/* DWCMSHC specific Mode Select value */
+#define DWCMSHC_CTRL_HS400             0x7
+
 #define BOUNDARY_OK(addr, len) \
        ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
 
@@ -46,10 +49,36 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
        sdhci_adma_write_desc(host, desc, addr, len, cmd);
 }
 
+static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
+                                     unsigned int timing)
+{
+       u16 ctrl_2;
+
+       ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+       /* Select Bus Speed Mode for host */
+       ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+       if ((timing == MMC_TIMING_MMC_HS200) ||
+           (timing == MMC_TIMING_UHS_SDR104))
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+       else if (timing == MMC_TIMING_UHS_SDR12)
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+       else if ((timing == MMC_TIMING_UHS_SDR25) ||
+                (timing == MMC_TIMING_MMC_HS))
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+       else if (timing == MMC_TIMING_UHS_SDR50)
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+       else if ((timing == MMC_TIMING_UHS_DDR50) ||
+                (timing == MMC_TIMING_MMC_DDR52))
+               ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+       else if (timing == MMC_TIMING_MMC_HS400)
+               ctrl_2 |= DWCMSHC_CTRL_HS400;
+       sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
 static const struct sdhci_ops sdhci_dwcmshc_ops = {
        .set_clock              = sdhci_set_clock,
        .set_bus_width          = sdhci_set_bus_width,
-       .set_uhs_signaling      = sdhci_set_uhs_signaling,
+       .set_uhs_signaling      = dwcmshc_set_uhs_signaling,
        .get_max_clock          = sdhci_pltfm_clk_get_max_clock,
        .reset                  = sdhci_reset,
        .adma_write_desc        = dwcmshc_adma_write_desc,
@@ -134,6 +163,48 @@ static int dwcmshc_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int dwcmshc_suspend(struct device *dev)
+{
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+       int ret;
+
+       ret = sdhci_suspend_host(host);
+       if (ret)
+               return ret;
+
+       clk_disable_unprepare(pltfm_host->clk);
+       if (!IS_ERR(priv->bus_clk))
+               clk_disable_unprepare(priv->bus_clk);
+
+       return ret;
+}
+
+static int dwcmshc_resume(struct device *dev)
+{
+       struct sdhci_host *host = dev_get_drvdata(dev);
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+       int ret;
+
+       ret = clk_prepare_enable(pltfm_host->clk);
+       if (ret)
+               return ret;
+
+       if (!IS_ERR(priv->bus_clk)) {
+               ret = clk_prepare_enable(priv->bus_clk);
+               if (ret)
+                       return ret;
+       }
+
+       return sdhci_resume_host(host);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
+
 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
        { .compatible = "snps,dwcmshc-sdhci" },
        {}
@@ -144,6 +215,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
        .driver = {
                .name   = "sdhci-dwcmshc",
                .of_match_table = sdhci_dwcmshc_dt_ids,
+               .pm = &dwcmshc_pmops,
        },
        .probe  = dwcmshc_probe,
        .remove = dwcmshc_remove,
index 5d8dd870bd4429cd40cb17d56614eceba972c8d8..7c73d243dc6ceeb0a1a3b632d2905157372a0793 100644 (file)
@@ -1135,6 +1135,40 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
 static void esdhc_set_uhs_signaling(struct sdhci_host *host,
                                   unsigned int timing)
 {
+       u32 val;
+
+       /*
+        * There are specific registers setting for HS400 mode.
+        * Clean all of them if controller is in HS400 mode to
+        * exit HS400 mode before re-setting any speed mode.
+        */
+       val = sdhci_readl(host, ESDHC_TBCTL);
+       if (val & ESDHC_HS400_MODE) {
+               val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
+               val &= ~ESDHC_FLW_CTL_BG;
+               sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
+
+               val = sdhci_readl(host, ESDHC_SDCLKCTL);
+               val &= ~ESDHC_CMD_CLK_CTL;
+               sdhci_writel(host, val, ESDHC_SDCLKCTL);
+
+               esdhc_clock_enable(host, false);
+               val = sdhci_readl(host, ESDHC_TBCTL);
+               val &= ~ESDHC_HS400_MODE;
+               sdhci_writel(host, val, ESDHC_TBCTL);
+               esdhc_clock_enable(host, true);
+
+               val = sdhci_readl(host, ESDHC_DLLCFG0);
+               val &= ~(ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL);
+               sdhci_writel(host, val, ESDHC_DLLCFG0);
+
+               val = sdhci_readl(host, ESDHC_TBCTL);
+               val &= ~ESDHC_HS400_WNDW_ADJUST;
+               sdhci_writel(host, val, ESDHC_TBCTL);
+
+               esdhc_tuning_block_enable(host, false);
+       }
+
        if (timing == MMC_TIMING_MMC_HS400)
                esdhc_tuning_block_enable(host, true);
        else
index 2527244c2ae16f1e7c2c11491c4c3be3a49fc9f9..bb6802448b2f4a9573f083da896a85aa0d225010 100644 (file)
@@ -249,12 +249,8 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
 static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
 {
        slot->host->caps =
-               ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
-                       & SDHCI_TIMEOUT_CLK_MASK) |
-
-               ((0x21 << SDHCI_CLOCK_BASE_SHIFT)
-                       & SDHCI_CLOCK_BASE_MASK) |
-
+               FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
+               FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
                SDHCI_TIMEOUT_CLK_UNIT |
                SDHCI_CAN_VDD_330 |
                SDHCI_CAN_DO_HISPD |
@@ -1749,6 +1745,7 @@ static const struct pci_device_id pci_ids[] = {
        SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
        SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
        SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
+       SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e),
        SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
        /* Generic SD host controller */
        {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
index fd76aa672e020445a2ee3777ab0cf623bb8b436c..ca0166d9bf82355cf445f34ccdaa2a44873d7397 100644 (file)
 #define   SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY    GENMASK(2, 0)
 #define   GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE    0x1
 
+#define SDHCI_GLI_9763E_CTRL_HS400  0x7
+
+#define SDHCI_GLI_9763E_HS400_ES_REG      0x52C
+#define   SDHCI_GLI_9763E_HS400_ES_BIT      BIT(8)
+
+#define PCIE_GLI_9763E_VHS      0x884
+#define   GLI_9763E_VHS_REV       GENMASK(19, 16)
+#define   GLI_9763E_VHS_REV_R      0x0
+#define   GLI_9763E_VHS_REV_M      0x1
+#define   GLI_9763E_VHS_REV_W      0x2
+#define PCIE_GLI_9763E_SCR      0x8E0
+#define   GLI_9763E_SCR_AXI_REQ           BIT(9)
+
 #define GLI_MAX_TUNING_LOOP 40
 
 /* Genesys Logic chipset */
@@ -351,6 +364,81 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
 }
 #endif
 
+static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
+                                         struct mmc_ios *ios)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+       u32 val;
+
+       val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG);
+       if (ios->enhanced_strobe)
+               val |= SDHCI_GLI_9763E_HS400_ES_BIT;
+       else
+               val &= ~SDHCI_GLI_9763E_HS400_ES_BIT;
+
+       sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG);
+}
+
+static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
+                                       unsigned int timing)
+{
+       u16 ctrl_2;
+
+       ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+       ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+       if (timing == MMC_TIMING_MMC_HS200)
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+       else if (timing == MMC_TIMING_MMC_HS)
+               ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+       else if (timing == MMC_TIMING_MMC_DDR52)
+               ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+       else if (timing == MMC_TIMING_MMC_HS400)
+               ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400;
+
+       sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
+{
+       struct pci_dev *pdev = slot->chip->pdev;
+       u32 value;
+
+       pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
+       value &= ~GLI_9763E_VHS_REV;
+       value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
+       pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
+
+       pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value);
+       value |= GLI_9763E_SCR_AXI_REQ;
+       pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value);
+
+       pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
+       value &= ~GLI_9763E_VHS_REV;
+       value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
+       pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
+}
+
+static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
+{
+       struct sdhci_host *host = slot->host;
+
+       host->mmc->caps |= MMC_CAP_8_BIT_DATA |
+                          MMC_CAP_1_8V_DDR |
+                          MMC_CAP_NONREMOVABLE;
+       host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR |
+                           MMC_CAP2_HS400_1_8V |
+                           MMC_CAP2_HS400_ES |
+                           MMC_CAP2_NO_SDIO |
+                           MMC_CAP2_NO_SD;
+       gli_pcie_enable_msi(slot);
+       host->mmc_host_ops.hs400_enhanced_strobe =
+                                       gl9763e_hs400_enhanced_strobe;
+       gli_set_gl9763e(slot);
+       sdhci_enable_v4_mode(host);
+
+       return 0;
+}
+
 static const struct sdhci_ops sdhci_gl9755_ops = {
        .set_clock              = sdhci_set_clock,
        .enable_dma             = sdhci_pci_enable_dma,
@@ -390,3 +478,21 @@ const struct sdhci_pci_fixes sdhci_gl9750 = {
        .resume         = sdhci_pci_gli_resume,
 #endif
 };
+
+static const struct sdhci_ops sdhci_gl9763e_ops = {
+       .set_clock              = sdhci_set_clock,
+       .enable_dma             = sdhci_pci_enable_dma,
+       .set_bus_width          = sdhci_set_bus_width,
+       .reset                  = sdhci_reset,
+       .set_uhs_signaling      = sdhci_set_gl9763e_signaling,
+       .voltage_switch         = sdhci_gli_voltage_switch,
+};
+
+const struct sdhci_pci_fixes sdhci_gl9763e = {
+       .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
+       .probe_slot     = gli_probe_slot_gl9763e,
+       .ops            = &sdhci_gl9763e_ops,
+#ifdef CONFIG_PM_SLEEP
+       .resume         = sdhci_pci_gli_resume,
+#endif
+};
index fa8105087d6848b0ddbfc25c629f14a3ab7c4d74..e2a846885902f591a3f5113c453c9542a89a53ac 100644 (file)
@@ -494,7 +494,7 @@ static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk)
        }
 }
 
-void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
+static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
 {
        u16 clk;
 
@@ -509,7 +509,7 @@ void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
        sdhci_o2_enable_clk(host, clk);
 }
 
-int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
+static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
 {
        struct sdhci_pci_chip *chip;
        struct sdhci_host *host;
@@ -578,7 +578,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
        return 0;
 }
 
-int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
+static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
 {
        int ret;
        u8 scratch;
@@ -783,7 +783,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
 }
 
 #ifdef CONFIG_PM_SLEEP
-int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
+static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
 {
        sdhci_pci_o2_probe(chip);
        return sdhci_pci_resume_host(chip);
index 42ccd123b04689e6e1d88632f0b00bce705ef223..d0ed232af0eb84f2a4295780af47c1ceb01e7b89 100644 (file)
@@ -72,6 +72,7 @@
 
 #define PCI_DEVICE_ID_GLI_9755         0x9755
 #define PCI_DEVICE_ID_GLI_9750         0x9750
+#define PCI_DEVICE_ID_GLI_9763E                0xe763
 
 /*
  * PCI device class and mask
@@ -195,5 +196,6 @@ extern const struct sdhci_pci_fixes sdhci_snps;
 extern const struct sdhci_pci_fixes sdhci_o2;
 extern const struct sdhci_pci_fixes sdhci_gl9750;
 extern const struct sdhci_pci_fixes sdhci_gl9755;
+extern const struct sdhci_pci_fixes sdhci_gl9763e;
 
 #endif /* __SDHCI_PCI_H */
index 2ab42c59e4f88a50a60e4693dc00e2911cf41c85..a910cb461ed7c1c280027e94bdb51b3a1e3b9caf 100644 (file)
@@ -406,7 +406,8 @@ static struct sdhci_ops sdhci_sprd_ops = {
        .request_done = sdhci_sprd_request_done,
 };
 
-static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+static void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc,
+                                       struct mmc_request *mrq)
 {
        struct sdhci_host *host = mmc_priv(mmc);
        struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
@@ -422,10 +423,23 @@ static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
            mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
            (host->flags & SDHCI_AUTO_CMD23))
                host->flags &= ~SDHCI_AUTO_CMD23;
+}
+
+static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       sdhci_sprd_check_auto_cmd23(mmc, mrq);
 
        sdhci_request(mmc, mrq);
 }
 
+static int sdhci_sprd_request_atomic(struct mmc_host *mmc,
+                                     struct mmc_request *mrq)
+{
+       sdhci_sprd_check_auto_cmd23(mmc, mrq);
+
+       return sdhci_request_atomic(mmc, mrq);
+}
+
 static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct sdhci_host *host = mmc_priv(mmc);
@@ -434,7 +448,7 @@ static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
 
        if (!IS_ERR(mmc->supply.vqmmc)) {
                ret = mmc_regulator_set_vqmmc(mmc, ios);
-               if (ret) {
+               if (ret < 0) {
                        pr_err("%s: Switching signalling voltage failed\n",
                               mmc_hostname(mmc));
                        return ret;
@@ -556,11 +570,17 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
                sdhci_sprd_voltage_switch;
 
        host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
-               MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
+               MMC_CAP_WAIT_WHILE_BUSY;
+
        ret = mmc_of_parse(host->mmc);
        if (ret)
                goto pltfm_free;
 
+       if (!mmc_card_is_removable(host->mmc))
+               host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic;
+       else
+               host->always_defer_done = true;
+
        sprd_host = TO_SPRD_HOST(host);
        sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node);
 
@@ -654,8 +674,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
        if (ret)
                goto err_cleanup_host;
 
-       host->always_defer_done = true;
-
        ret = __sdhci_add_host(host);
        if (ret)
                goto err_cleanup_host;
index 3e2c5101291db369ec86bd056595f6e4fdddba72..3a372ab3d12e87785a6d4f5e0adffe83b0f326d6 100644 (file)
@@ -604,6 +604,39 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
        if (err)
                autocal->pull_down_1v8 = 0;
 
+       err = device_property_read_u32(host->mmc->parent,
+                       "nvidia,pad-autocal-pull-up-offset-sdr104",
+                       &autocal->pull_up_sdr104);
+       if (err)
+               autocal->pull_up_sdr104 = autocal->pull_up_1v8;
+
+       err = device_property_read_u32(host->mmc->parent,
+                       "nvidia,pad-autocal-pull-down-offset-sdr104",
+                       &autocal->pull_down_sdr104);
+       if (err)
+               autocal->pull_down_sdr104 = autocal->pull_down_1v8;
+
+       err = device_property_read_u32(host->mmc->parent,
+                       "nvidia,pad-autocal-pull-up-offset-hs400",
+                       &autocal->pull_up_hs400);
+       if (err)
+               autocal->pull_up_hs400 = autocal->pull_up_1v8;
+
+       err = device_property_read_u32(host->mmc->parent,
+                       "nvidia,pad-autocal-pull-down-offset-hs400",
+                       &autocal->pull_down_hs400);
+       if (err)
+               autocal->pull_down_hs400 = autocal->pull_down_1v8;
+
+       /*
+        * Different fail-safe drive strength values based on the signaling
+        * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls.
+        * So, avoid reading below device tree properties for SoCs that don't
+        * have NVQUIRK_NEEDS_PAD_CONTROL.
+        */
+       if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
+               return;
+
        err = device_property_read_u32(host->mmc->parent,
                        "nvidia,pad-autocal-pull-up-offset-3v3-timeout",
                        &autocal->pull_up_3v3_timeout);
@@ -647,30 +680,6 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
                                mmc_hostname(host->mmc));
                autocal->pull_down_1v8_timeout = 0;
        }
-
-       err = device_property_read_u32(host->mmc->parent,
-                       "nvidia,pad-autocal-pull-up-offset-sdr104",
-                       &autocal->pull_up_sdr104);
-       if (err)
-               autocal->pull_up_sdr104 = autocal->pull_up_1v8;
-
-       err = device_property_read_u32(host->mmc->parent,
-                       "nvidia,pad-autocal-pull-down-offset-sdr104",
-                       &autocal->pull_down_sdr104);
-       if (err)
-               autocal->pull_down_sdr104 = autocal->pull_down_1v8;
-
-       err = device_property_read_u32(host->mmc->parent,
-                       "nvidia,pad-autocal-pull-up-offset-hs400",
-                       &autocal->pull_up_hs400);
-       if (err)
-               autocal->pull_up_hs400 = autocal->pull_up_1v8;
-
-       err = device_property_read_u32(host->mmc->parent,
-                       "nvidia,pad-autocal-pull-down-offset-hs400",
-                       &autocal->pull_down_hs400);
-       if (err)
-               autocal->pull_down_hs400 = autocal->pull_down_1v8;
 }
 
 static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
index 3f716466fcfd04a1ed7b0e992ee9a072a9126104..37b1158c1c0c9427d272b45a24c4049de5ff702e 100644 (file)
 static unsigned int debug_quirks = 0;
 static unsigned int debug_quirks2;
 
-static void sdhci_finish_data(struct sdhci_host *);
-
 static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
 
+static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
+
 void sdhci_dumpregs(struct sdhci_host *host)
 {
        SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
@@ -111,6 +111,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
                }
        }
 
+       if (host->ops->dump_vendor_regs)
+               host->ops->dump_vendor_regs(host);
+
        SDHCI_DUMP("============================================\n");
 }
 EXPORT_SYMBOL_GPL(sdhci_dumpregs);
@@ -317,6 +320,7 @@ out:
 static void sdhci_init(struct sdhci_host *host, int soft)
 {
        struct mmc_host *mmc = host->mmc;
+       unsigned long flags;
 
        if (soft)
                sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
@@ -326,7 +330,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
        if (host->v4_mode)
                sdhci_do_enable_v4_mode(host);
 
+       spin_lock_irqsave(&host->lock, flags);
        sdhci_set_default_irqs(host);
+       spin_unlock_irqrestore(&host->lock, flags);
 
        host->cqe_on = false;
 
@@ -634,9 +640,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
                }
                if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
                        /* Copy the data to the bounce buffer */
-                       sg_copy_to_buffer(data->sg, data->sg_len,
-                                         host->bounce_buffer,
-                                         length);
+                       if (host->ops->copy_to_bounce_buffer) {
+                               host->ops->copy_to_bounce_buffer(host,
+                                                                data, length);
+                       } else {
+                               sg_copy_to_buffer(data->sg, data->sg_len,
+                                                 host->bounce_buffer, length);
+                       }
                }
                /* Switch ownership to the DMA */
                dma_sync_single_for_device(host->mmc->parent,
@@ -1350,13 +1360,25 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
               !mrq->cap_cmd_during_tfr;
 }
 
+static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
+                                   struct mmc_request *mrq)
+{
+       return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
+}
+
+static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
+                                     struct mmc_request *mrq)
+{
+       return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
+}
+
 static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
                                         struct mmc_command *cmd,
                                         u16 *mode)
 {
        bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) &&
                         (cmd->opcode != SD_IO_RW_EXTENDED);
-       bool use_cmd23 = cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
+       bool use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq);
        u16 ctrl2;
 
        /*
@@ -1416,7 +1438,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
        if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
                mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
                sdhci_auto_cmd_select(host, cmd, &mode);
-               if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23))
+               if (sdhci_auto_cmd23(host, cmd->mrq))
                        sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
        }
 
@@ -1466,6 +1488,9 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
        if (host->data_cmd && host->data_cmd->mrq == mrq)
                host->data_cmd = NULL;
 
+       if (host->deferred_cmd && host->deferred_cmd->mrq == mrq)
+               host->deferred_cmd = NULL;
+
        if (host->data && host->data->mrq == mrq)
                host->data = NULL;
 
@@ -1487,7 +1512,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
        queue_work(host->complete_wq, &host->complete_work);
 }
 
-static void sdhci_finish_data(struct sdhci_host *host)
+static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
 {
        struct mmc_command *data_cmd = host->data_cmd;
        struct mmc_data *data = host->data;
@@ -1539,14 +1564,31 @@ static void sdhci_finish_data(struct sdhci_host *host)
                } else {
                        /* Avoid triggering warning in sdhci_send_command() */
                        host->cmd = NULL;
-                       sdhci_send_command(host, data->stop);
+                       if (!sdhci_send_command(host, data->stop)) {
+                               if (sw_data_timeout) {
+                                       /*
+                                        * This is anyway a sw data timeout, so
+                                        * give up now.
+                                        */
+                                       data->stop->error = -EIO;
+                                       __sdhci_finish_mrq(host, data->mrq);
+                               } else {
+                                       WARN_ON(host->deferred_cmd);
+                                       host->deferred_cmd = data->stop;
+                               }
+                       }
                }
        } else {
                __sdhci_finish_mrq(host, data->mrq);
        }
 }
 
-void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+       __sdhci_finish_data(host, false);
+}
+
+static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
        u32 mask;
@@ -1561,9 +1603,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
            cmd->opcode == MMC_STOP_TRANSMISSION)
                cmd->flags |= MMC_RSP_BUSY;
 
-       /* Wait max 10 ms */
-       timeout = 10;
-
        mask = SDHCI_CMD_INHIBIT;
        if (sdhci_data_line_cmd(cmd))
                mask |= SDHCI_DATA_INHIBIT;
@@ -1573,18 +1612,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
                mask &= ~SDHCI_DATA_INHIBIT;
 
-       while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
-               if (timeout == 0) {
-                       pr_err("%s: Controller never released inhibit bit(s).\n",
-                              mmc_hostname(host->mmc));
-                       sdhci_dumpregs(host);
-                       cmd->error = -EIO;
-                       sdhci_finish_mrq(host, cmd->mrq);
-                       return;
-               }
-               timeout--;
-               mdelay(1);
-       }
+       if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
+               return false;
 
        host->cmd = cmd;
        host->data_timeout = 0;
@@ -1606,11 +1635,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        sdhci_set_transfer_mode(host, cmd);
 
        if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
-               pr_err("%s: Unsupported response type!\n",
-                       mmc_hostname(host->mmc));
-               cmd->error = -EINVAL;
-               sdhci_finish_mrq(host, cmd->mrq);
-               return;
+               WARN_ONCE(1, "Unsupported response type!\n");
+               /*
+                * This does not happen in practice because 136-bit response
+                * commands never have busy waiting, so rather than complicate
+                * the error path, just remove busy waiting and continue.
+                */
+               cmd->flags &= ~MMC_RSP_BUSY;
        }
 
        if (!(cmd->flags & MMC_RSP_PRESENT))
@@ -1645,8 +1676,61 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
                sdhci_external_dma_pre_transfer(host, cmd);
 
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+
+       return true;
+}
+
+static bool sdhci_present_error(struct sdhci_host *host,
+                               struct mmc_command *cmd, bool present)
+{
+       if (!present || host->flags & SDHCI_DEVICE_DEAD) {
+               cmd->error = -ENOMEDIUM;
+               return true;
+       }
+
+       return false;
+}
+
+static bool sdhci_send_command_retry(struct sdhci_host *host,
+                                    struct mmc_command *cmd,
+                                    unsigned long flags)
+       __releases(host->lock)
+       __acquires(host->lock)
+{
+       struct mmc_command *deferred_cmd = host->deferred_cmd;
+       int timeout = 10; /* Approx. 10 ms */
+       bool present;
+
+       while (!sdhci_send_command(host, cmd)) {
+               if (!timeout--) {
+                       pr_err("%s: Controller never released inhibit bit(s).\n",
+                              mmc_hostname(host->mmc));
+                       sdhci_dumpregs(host);
+                       cmd->error = -EIO;
+                       return false;
+               }
+
+               spin_unlock_irqrestore(&host->lock, flags);
+
+               usleep_range(1000, 1250);
+
+               present = host->mmc->ops->get_cd(host->mmc);
+
+               spin_lock_irqsave(&host->lock, flags);
+
+               /* A deferred command might disappear, handle that */
+               if (cmd == deferred_cmd && cmd != host->deferred_cmd)
+                       return true;
+
+               if (sdhci_present_error(host, cmd, present))
+                       return false;
+       }
+
+       if (cmd == host->deferred_cmd)
+               host->deferred_cmd = NULL;
+
+       return true;
 }
-EXPORT_SYMBOL_GPL(sdhci_send_command);
 
 static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
 {
@@ -1707,7 +1791,10 @@ static void sdhci_finish_command(struct sdhci_host *host)
 
        /* Finished CMD23, now send actual command. */
        if (cmd == cmd->mrq->sbc) {
-               sdhci_send_command(host, cmd->mrq->cmd);
+               if (!sdhci_send_command(host, cmd->mrq->cmd)) {
+                       WARN_ON(host->deferred_cmd);
+                       host->deferred_cmd = cmd->mrq->cmd;
+               }
        } else {
 
                /* Processed actual command. */
@@ -2037,11 +2124,10 @@ EXPORT_SYMBOL_GPL(sdhci_set_power_and_bus_voltage);
 
 void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
-       struct sdhci_host *host;
-       int present;
+       struct sdhci_host *host = mmc_priv(mmc);
+       struct mmc_command *cmd;
        unsigned long flags;
-
-       host = mmc_priv(mmc);
+       bool present;
 
        /* Firstly check card presence */
        present = mmc->ops->get_cd(mmc);
@@ -2050,19 +2136,57 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        sdhci_led_activate(host);
 
-       if (!present || host->flags & SDHCI_DEVICE_DEAD) {
-               mrq->cmd->error = -ENOMEDIUM;
+       if (sdhci_present_error(host, mrq->cmd, present))
+               goto out_finish;
+
+       cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
+
+       if (!sdhci_send_command_retry(host, cmd, flags))
+               goto out_finish;
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       return;
+
+out_finish:
+       sdhci_finish_mrq(host, mrq);
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sdhci_request);
+
+int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+       struct mmc_command *cmd;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       if (sdhci_present_error(host, mrq->cmd, true)) {
                sdhci_finish_mrq(host, mrq);
-       } else {
-               if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
-                       sdhci_send_command(host, mrq->sbc);
-               else
-                       sdhci_send_command(host, mrq->cmd);
+               goto out_finish;
        }
 
+       cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
+
+       /*
+        * The HSQ may send a command in interrupt context without polling
+        * the busy signaling, which means we should return BUSY if controller
+        * has not released inhibit bits to allow HSQ trying to send request
+        * again in non-atomic context. So we should not finish this request
+        * here.
+        */
+       if (!sdhci_send_command(host, cmd))
+               ret = -EBUSY;
+       else
+               sdhci_led_activate(host);
+
+out_finish:
        spin_unlock_irqrestore(&host->lock, flags);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(sdhci_request);
+EXPORT_SYMBOL_GPL(sdhci_request_atomic);
 
 void sdhci_set_bus_width(struct sdhci_host *host, int width)
 {
@@ -2411,7 +2535,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
 
                if (!IS_ERR(mmc->supply.vqmmc)) {
                        ret = mmc_regulator_set_vqmmc(mmc, ios);
-                       if (ret) {
+                       if (ret < 0) {
                                pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
                                        mmc_hostname(mmc));
                                return -EIO;
@@ -2434,7 +2558,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
                        return -EINVAL;
                if (!IS_ERR(mmc->supply.vqmmc)) {
                        ret = mmc_regulator_set_vqmmc(mmc, ios);
-                       if (ret) {
+                       if (ret < 0) {
                                pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
                                        mmc_hostname(mmc));
                                return -EIO;
@@ -2466,7 +2590,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
                        return -EINVAL;
                if (!IS_ERR(mmc->supply.vqmmc)) {
                        ret = mmc_regulator_set_vqmmc(mmc, ios);
-                       if (ret) {
+                       if (ret < 0) {
                                pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
                                        mmc_hostname(mmc));
                                return -EIO;
@@ -2600,7 +2724,11 @@ void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
         */
        sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
 
-       sdhci_send_command(host, &cmd);
+       if (!sdhci_send_command_retry(host, &cmd, flags)) {
+               spin_unlock_irqrestore(&host->lock, flags);
+               host->tuning_done = 0;
+               return;
+       }
 
        host->cmd = NULL;
 
@@ -3018,7 +3146,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
 
                if (host->data) {
                        host->data->error = -ETIMEDOUT;
-                       sdhci_finish_data(host);
+                       __sdhci_finish_data(host, true);
                        queue_work(host->complete_wq, &host->complete_work);
                } else if (host->data_cmd) {
                        host->data_cmd->error = -ETIMEDOUT;
@@ -3390,6 +3518,9 @@ cont:
                }
        }
 out:
+       if (host->deferred_cmd)
+               result = IRQ_WAKE_THREAD;
+
        spin_unlock(&host->lock);
 
        /* Process mrqs ready for immediate completion */
@@ -3415,6 +3546,7 @@ out:
 static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
 {
        struct sdhci_host *host = dev_id;
+       struct mmc_command *cmd;
        unsigned long flags;
        u32 isr;
 
@@ -3422,8 +3554,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
                ;
 
        spin_lock_irqsave(&host->lock, flags);
+
        isr = host->thread_isr;
        host->thread_isr = 0;
+
+       cmd = host->deferred_cmd;
+       if (cmd && !sdhci_send_command_retry(host, cmd, flags))
+               sdhci_finish_mrq(host, cmd->mrq);
+
        spin_unlock_irqrestore(&host->lock, flags);
 
        if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
@@ -4000,9 +4138,6 @@ int sdhci_setup_host(struct sdhci_host *host)
                       mmc_hostname(mmc), host->version);
        }
 
-       if (host->quirks & SDHCI_QUIRK_BROKEN_CQE)
-               mmc->caps2 &= ~MMC_CAP2_CQE;
-
        if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
                host->flags |= SDHCI_USE_SDMA;
        else if (!(host->caps & SDHCI_CAN_DO_SDMA))
@@ -4117,11 +4252,9 @@ int sdhci_setup_host(struct sdhci_host *host)
        }
 
        if (host->version >= SDHCI_SPEC_300)
-               host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
-                       >> SDHCI_CLOCK_BASE_SHIFT;
+               host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
        else
-               host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
-                       >> SDHCI_CLOCK_BASE_SHIFT;
+               host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps);
 
        host->max_clk *= 1000000;
        if (host->max_clk == 0 || host->quirks &
@@ -4139,8 +4272,7 @@ int sdhci_setup_host(struct sdhci_host *host)
         * In case of Host Controller v3.00, find out whether clock
         * multiplier is supported.
         */
-       host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
-                       SDHCI_CLOCK_MUL_SHIFT;
+       host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1);
 
        /*
         * In case the value in Clock Multiplier is 0, then programmable
@@ -4173,8 +4305,7 @@ int sdhci_setup_host(struct sdhci_host *host)
                mmc->f_max = max_clk;
 
        if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
-               host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
-                                       SDHCI_TIMEOUT_CLK_SHIFT;
+               host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
 
                if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
                        host->timeout_clk *= 1000;
@@ -4204,7 +4335,7 @@ int sdhci_setup_host(struct sdhci_host *host)
            !host->ops->get_max_timeout_count)
                mmc->max_busy_timeout = 0;
 
-       mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
+       mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
        mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
 
        if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
@@ -4326,8 +4457,8 @@ int sdhci_setup_host(struct sdhci_host *host)
                mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
 
        /* Initial value for re-tuning timer count */
-       host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
-                            SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+       host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
+                                      host->caps1);
 
        /*
         * In case Re-tuning Timer is not disabled, the actual value of
@@ -4337,8 +4468,7 @@ int sdhci_setup_host(struct sdhci_host *host)
                host->tuning_count = 1 << (host->tuning_count - 1);
 
        /* Re-tuning mode supported by the Host Controller */
-       host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
-                            SDHCI_RETUNING_MODE_SHIFT;
+       host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
 
        ocr_avail = 0;
 
@@ -4360,35 +4490,32 @@ int sdhci_setup_host(struct sdhci_host *host)
 
                        curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
                        max_current_caps =
-                               (curr << SDHCI_MAX_CURRENT_330_SHIFT) |
-                               (curr << SDHCI_MAX_CURRENT_300_SHIFT) |
-                               (curr << SDHCI_MAX_CURRENT_180_SHIFT);
+                               FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, curr) |
+                               FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, curr) |
+                               FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, curr);
                }
        }
 
        if (host->caps & SDHCI_CAN_VDD_330) {
                ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
 
-               mmc->max_current_330 = ((max_current_caps &
-                                  SDHCI_MAX_CURRENT_330_MASK) >>
-                                  SDHCI_MAX_CURRENT_330_SHIFT) *
-                                  SDHCI_MAX_CURRENT_MULTIPLIER;
+               mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
+                                                max_current_caps) *
+                                               SDHCI_MAX_CURRENT_MULTIPLIER;
        }
        if (host->caps & SDHCI_CAN_VDD_300) {
                ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
 
-               mmc->max_current_300 = ((max_current_caps &
-                                  SDHCI_MAX_CURRENT_300_MASK) >>
-                                  SDHCI_MAX_CURRENT_300_SHIFT) *
-                                  SDHCI_MAX_CURRENT_MULTIPLIER;
+               mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
+                                                max_current_caps) *
+                                               SDHCI_MAX_CURRENT_MULTIPLIER;
        }
        if (host->caps & SDHCI_CAN_VDD_180) {
                ocr_avail |= MMC_VDD_165_195;
 
-               mmc->max_current_180 = ((max_current_caps &
-                                  SDHCI_MAX_CURRENT_180_MASK) >>
-                                  SDHCI_MAX_CURRENT_180_SHIFT) *
-                                  SDHCI_MAX_CURRENT_MULTIPLIER;
+               mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
+                                                max_current_caps) *
+                                               SDHCI_MAX_CURRENT_MULTIPLIER;
        }
 
        /* If OCR set by host, use it instead. */
@@ -4539,6 +4666,12 @@ int __sdhci_add_host(struct sdhci_host *host)
        struct mmc_host *mmc = host->mmc;
        int ret;
 
+       if ((mmc->caps2 & MMC_CAP2_CQE) &&
+           (host->quirks & SDHCI_QUIRK_BROKEN_CQE)) {
+               mmc->caps2 &= ~MMC_CAP2_CQE;
+               mmc->cqe_ops = NULL;
+       }
+
        host->complete_wq = alloc_workqueue("sdhci", flags, 0);
        if (!host->complete_wq)
                return -ENOMEM;
index 79dffbb731d3349095733ab418722566df4b7a79..0008bbd27127336b233b96f8591d253b8268d619 100644 (file)
 #define  SDHCI_CTRL_PRESET_VAL_ENABLE  0x8000
 
 #define SDHCI_CAPABILITIES     0x40
-#define  SDHCI_TIMEOUT_CLK_MASK        0x0000003F
-#define  SDHCI_TIMEOUT_CLK_SHIFT 0
+#define  SDHCI_TIMEOUT_CLK_MASK                GENMASK(5, 0)
 #define  SDHCI_TIMEOUT_CLK_UNIT        0x00000080
-#define  SDHCI_CLOCK_BASE_MASK 0x00003F00
-#define  SDHCI_CLOCK_V3_BASE_MASK      0x0000FF00
-#define  SDHCI_CLOCK_BASE_SHIFT        8
+#define  SDHCI_CLOCK_BASE_MASK         GENMASK(13, 8)
+#define  SDHCI_CLOCK_V3_BASE_MASK      GENMASK(15, 8)
 #define  SDHCI_MAX_BLOCK_MASK  0x00030000
 #define  SDHCI_MAX_BLOCK_SHIFT  16
 #define  SDHCI_CAN_DO_8BIT     0x00040000
 #define  SDHCI_CAN_64BIT_V4    0x08000000
 #define  SDHCI_CAN_64BIT       0x10000000
 
+#define SDHCI_CAPABILITIES_1   0x44
 #define  SDHCI_SUPPORT_SDR50   0x00000001
 #define  SDHCI_SUPPORT_SDR104  0x00000002
 #define  SDHCI_SUPPORT_DDR50   0x00000004
 #define  SDHCI_DRIVER_TYPE_A   0x00000010
 #define  SDHCI_DRIVER_TYPE_C   0x00000020
 #define  SDHCI_DRIVER_TYPE_D   0x00000040
-#define  SDHCI_RETUNING_TIMER_COUNT_MASK       0x00000F00
-#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT      8
+#define  SDHCI_RETUNING_TIMER_COUNT_MASK       GENMASK(11, 8)
 #define  SDHCI_USE_SDR50_TUNING                        0x00002000
-#define  SDHCI_RETUNING_MODE_MASK              0x0000C000
-#define  SDHCI_RETUNING_MODE_SHIFT             14
-#define  SDHCI_CLOCK_MUL_MASK  0x00FF0000
-#define  SDHCI_CLOCK_MUL_SHIFT 16
+#define  SDHCI_RETUNING_MODE_MASK              GENMASK(15, 14)
+#define  SDHCI_CLOCK_MUL_MASK                  GENMASK(23, 16)
 #define  SDHCI_CAN_DO_ADMA3    0x08000000
 #define  SDHCI_SUPPORT_HS400   0x80000000 /* Non-standard */
 
-#define SDHCI_CAPABILITIES_1   0x44
-
 #define SDHCI_MAX_CURRENT              0x48
-#define  SDHCI_MAX_CURRENT_LIMIT       0xFF
-#define  SDHCI_MAX_CURRENT_330_MASK    0x0000FF
-#define  SDHCI_MAX_CURRENT_330_SHIFT   0
-#define  SDHCI_MAX_CURRENT_300_MASK    0x00FF00
-#define  SDHCI_MAX_CURRENT_300_SHIFT   8
-#define  SDHCI_MAX_CURRENT_180_MASK    0xFF0000
-#define  SDHCI_MAX_CURRENT_180_SHIFT   16
+#define  SDHCI_MAX_CURRENT_LIMIT       GENMASK(7, 0)
+#define  SDHCI_MAX_CURRENT_330_MASK    GENMASK(7, 0)
+#define  SDHCI_MAX_CURRENT_300_MASK    GENMASK(15, 8)
+#define  SDHCI_MAX_CURRENT_180_MASK    GENMASK(23, 16)
 #define   SDHCI_MAX_CURRENT_MULTIPLIER 4
 
 /* 4C-4F reserved for more max current */
@@ -540,6 +531,7 @@ struct sdhci_host {
        struct mmc_request *mrqs_done[SDHCI_MAX_MRQS];  /* Requests done */
        struct mmc_command *cmd;        /* Current command */
        struct mmc_command *data_cmd;   /* Current data command */
+       struct mmc_command *deferred_cmd;       /* Deferred command */
        struct mmc_data *data;  /* Current data request */
        unsigned int data_early:1;      /* Data finished before cmd */
 
@@ -653,8 +645,12 @@ struct sdhci_ops {
        void    (*voltage_switch)(struct sdhci_host *host);
        void    (*adma_write_desc)(struct sdhci_host *host, void **desc,
                                   dma_addr_t addr, int len, unsigned int cmd);
+       void    (*copy_to_bounce_buffer)(struct sdhci_host *host,
+                                        struct mmc_data *data,
+                                        unsigned int length);
        void    (*request_done)(struct sdhci_host *host,
                                struct mmc_request *mrq);
+       void    (*dump_vendor_regs)(struct sdhci_host *host);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -757,7 +753,6 @@ void sdhci_cleanup_host(struct sdhci_host *host);
 int __sdhci_add_host(struct sdhci_host *host);
 int sdhci_add_host(struct sdhci_host *host);
 void sdhci_remove_host(struct sdhci_host *host, int dead);
-void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
 
 static inline void sdhci_read_caps(struct sdhci_host *host)
 {
@@ -776,6 +771,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
 void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
                           unsigned short vdd);
 void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
+int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
 void sdhci_set_bus_width(struct sdhci_host *host, int width);
 void sdhci_reset(struct sdhci_host *host, u8 mask);
 void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
index a38b8b2a4e5c920809d9045f08ff7b8f7c15ff61..76a8cd3a186f6d8a2cf8246c0358eb1a01ed2747 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
+#include <linux/iopoll.h>
 #include <linux/scatterlist.h>
 
 #include <pcmcia/cistpl.h>
@@ -22,6 +23,7 @@
 #include <linux/io.h>
 
 #include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
 
 #define DRIVER_NAME "sdricoh_cs"
 
@@ -57,10 +59,8 @@ static unsigned int switchlocked;
 #define STATUS_BUSY              0x40000000
 
 /* timeouts */
-#define INIT_TIMEOUT      100
-#define CMD_TIMEOUT       100000
-#define TRANSFER_TIMEOUT  100000
-#define BUSY_TIMEOUT      32767
+#define SDRICOH_CMD_TIMEOUT_US 1000000
+#define SDRICOH_DATA_TIMEOUT_US        1000000
 
 /* list of supported pcmcia devices */
 static const struct pcmcia_device_id pcmcia_ids[] = {
@@ -124,19 +124,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
        return value;
 }
 
-static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
-                               unsigned int timeout){
-       unsigned int loop;
+static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status,
+                             unsigned int wanted)
+{
+       sdricoh_writel(host, R2E4_STATUS_RESP, status);
+       return status & wanted;
+}
+
+static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted)
+{
+       int ret;
        unsigned int status = 0;
        struct device *dev = host->dev;
-       for (loop = 0; loop < timeout; loop++) {
-               status = sdricoh_readl(host, R21C_STATUS);
-               sdricoh_writel(host, R2E4_STATUS_RESP, status);
-               if (status & wanted)
-                       break;
-       }
 
-       if (loop == timeout) {
+       ret = read_poll_timeout(sdricoh_readl, status,
+                               sdricoh_status_ok(host, status, wanted),
+                               32, SDRICOH_DATA_TIMEOUT_US, false,
+                               host, R21C_STATUS);
+       if (ret) {
                dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
                return -ETIMEDOUT;
        }
@@ -150,35 +155,46 @@ static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
 
 }
 
-static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
-                          unsigned int arg)
+static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd)
 {
-       unsigned int status;
-       int result = 0;
-       unsigned int loop = 0;
+       unsigned int status, timeout_us;
+       int ret;
+       unsigned char opcode = cmd->opcode;
+
        /* reset status reg? */
        sdricoh_writel(host, R21C_STATUS, 0x18);
+
+       /* MMC_APP_CMDs need some special handling */
+       if (host->app_cmd) {
+               opcode |= 64;
+               host->app_cmd = 0;
+       } else if (opcode == MMC_APP_CMD)
+               host->app_cmd = 1;
+
        /* fill parameters */
-       sdricoh_writel(host, R204_CMD_ARG, arg);
+       sdricoh_writel(host, R204_CMD_ARG, cmd->arg);
        sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
+
        /* wait for command completion */
-       if (opcode) {
-               for (loop = 0; loop < CMD_TIMEOUT; loop++) {
-                       status = sdricoh_readl(host, R21C_STATUS);
-                       sdricoh_writel(host, R2E4_STATUS_RESP, status);
-                       if (status  & STATUS_CMD_FINISHED)
-                               break;
-               }
-               /* don't check for timeout in the loop it is not always
-                  reset correctly
-               */
-               if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
-                       result = -ETIMEDOUT;
+       if (!opcode)
+               return 0;
 
-       }
+       timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 :
+               SDRICOH_CMD_TIMEOUT_US;
 
-       return result;
+       ret = read_poll_timeout(sdricoh_readl, status,
+                       sdricoh_status_ok(host, status, STATUS_CMD_FINISHED),
+                       32, timeout_us, false,
+                       host, R21C_STATUS);
+
+       /*
+        * Don't check for timeout status in the loop, as it's not always reset
+        * correctly.
+        */
+       if (ret || status & STATUS_CMD_TIMEOUT)
+               return -ETIMEDOUT;
 
+       return 0;
 }
 
 static int sdricoh_reset(struct sdricoh_host *host)
@@ -207,8 +223,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
        u32 data = 0;
        /* wait until the data is available */
        if (read) {
-               if (sdricoh_query_status(host, STATUS_READY_TO_READ,
-                                               TRANSFER_TIMEOUT))
+               if (sdricoh_query_status(host, STATUS_READY_TO_READ))
                        return -ETIMEDOUT;
                sdricoh_writel(host, R21C_STATUS, 0x18);
                /* read data */
@@ -224,8 +239,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
                        }
                }
        } else {
-               if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
-                                               TRANSFER_TIMEOUT))
+               if (sdricoh_query_status(host, STATUS_READY_TO_WRITE))
                        return -ETIMEDOUT;
                sdricoh_writel(host, R21C_STATUS, 0x18);
                /* write data */
@@ -251,28 +265,20 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
        struct mmc_command *cmd = mrq->cmd;
        struct mmc_data *data = cmd->data;
        struct device *dev = host->dev;
-       unsigned char opcode = cmd->opcode;
        int i;
 
        dev_dbg(dev, "=============================\n");
-       dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
+       dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode);
 
        sdricoh_writel(host, R21C_STATUS, 0x18);
 
-       /* MMC_APP_CMDs need some special handling */
-       if (host->app_cmd) {
-               opcode |= 64;
-               host->app_cmd = 0;
-       } else if (opcode == 55)
-               host->app_cmd = 1;
-
        /* read/write commands seem to require this */
        if (data) {
                sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
                sdricoh_writel(host, R208_DATAIO, 0);
        }
 
-       cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
+       cmd->error = sdricoh_mmc_cmd(host, cmd);
 
        /* read response buffer */
        if (cmd->flags & MMC_RSP_PRESENT) {
@@ -323,8 +329,7 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
                sdricoh_writel(host, R208_DATAIO, 1);
 
-               if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
-                                       TRANSFER_TIMEOUT)) {
+               if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) {
                        dev_err(dev, "sdricoh_request: transfer end error\n");
                        cmd->error = -EINVAL;
                }
index f87d7967457f6dbb736f5f797aec563a43222c70..5e95bbc516444677336a5d657f780bba12c38d08 100644 (file)
@@ -951,9 +951,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
 {
+       int ret;
+
        /* vqmmc regulator is available */
-       if (!IS_ERR(mmc->supply.vqmmc))
-               return mmc_regulator_set_vqmmc(mmc, ios);
+       if (!IS_ERR(mmc->supply.vqmmc)) {
+               ret = mmc_regulator_set_vqmmc(mmc, ios);
+               return ret < 0 ? ret : 0;
+       }
 
        /* no vqmmc regulator, assume fixed regulator at 3/3.3V */
        if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
@@ -1390,7 +1394,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
        mmc->f_min              =   400000;
        mmc->f_max              = 52000000;
        mmc->caps              |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
-                                 MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
+                                 MMC_CAP_SDIO_IRQ;
 
        /*
         * Some H5 devices do not have signal traces precise enough to
index 54271b92ee5991ff5b11c1b10d7e5e285844391e..5987656e0474cd262234dbbb0b85b624a039513a 100644 (file)
@@ -73,6 +73,8 @@ module_param(fixed_timeout, bool, 0644);
 
 #define TIFM_MMCSD_MAX_BLOCK_SIZE  0x0800UL
 
+#define TIFM_MMCSD_REQ_TIMEOUT_MS  1000
+
 enum {
        CMD_READY    = 0x0001,
        FIFO_READY   = 0x0002,
@@ -959,7 +961,12 @@ static int tifm_sd_probe(struct tifm_dev *sock)
        host = mmc_priv(mmc);
        tifm_set_drvdata(sock, mmc);
        host->dev = sock;
-       host->timeout_jiffies = msecs_to_jiffies(1000);
+       host->timeout_jiffies = msecs_to_jiffies(TIFM_MMCSD_REQ_TIMEOUT_MS);
+       /*
+        * We use a fixed request timeout of 1s, hence inform the core about it.
+        * A future improvement should instead respect the cmd->busy_timeout.
+        */
+       mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
 
        tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
                     (unsigned long)host);
index 9520bd94cf435335434e254d343c248d2e2cbae7..d7fde57c78c103094923c53fd4000f6e7ef01cd0 100644 (file)
@@ -39,7 +39,6 @@
 #include <linux/module.h>
 #include <linux/pagemap.h>
 #include <linux/platform_device.h>
-#include <linux/pm_domain.h>
 #include <linux/pm_qos.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
@@ -1128,7 +1127,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
        if (ret == -EPROBE_DEFER)
                return ret;
 
-       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_4_BIT_DATA | pdata->capabilities;
+       mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
        mmc->caps2 |= pdata->capabilities2;
        mmc->max_segs = pdata->max_segs ? : 32;
        mmc->max_blk_size = TMIO_MAX_BLK_SIZE;
@@ -1192,7 +1191,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
        /* See if we also get DMA */
        tmio_mmc_request_dma(_host, pdata);
 
-       dev_pm_domain_start(&pdev->dev);
        pm_runtime_get_noresume(&pdev->dev);
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
@@ -1231,12 +1229,14 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
        cancel_work_sync(&host->done);
        cancel_delayed_work_sync(&host->delayed_reset_work);
        tmio_mmc_release_dma(host);
+       tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
 
-       pm_runtime_dont_use_autosuspend(&pdev->dev);
        if (host->native_hotplug)
                pm_runtime_put_noidle(&pdev->dev);
-       pm_runtime_put_sync(&pdev->dev);
+
        pm_runtime_disable(&pdev->dev);
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
+       pm_runtime_put_noidle(&pdev->dev);
 }
 EXPORT_SYMBOL_GPL(tmio_mmc_host_remove);
 
index a1683c49cb903b8ef5806d2d85cea45452054b5e..f82baf99fd6977aee824f585854709aa766d9048 100644 (file)
@@ -610,11 +610,6 @@ static int uniphier_sd_probe(struct platform_device *pdev)
                }
        }
 
-       ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
-                              dev_name(dev), host);
-       if (ret)
-               goto free_host;
-
        if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP)
                host->dma_ops = &uniphier_sd_internal_dma_ops;
        else
@@ -642,8 +637,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
        if (ret)
                goto free_host;
 
+       ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
+                              dev_name(dev), host);
+       if (ret)
+               goto remove_host;
+
        return 0;
 
+remove_host:
+       tmio_mmc_host_remove(host);
 free_host:
        tmio_mmc_host_free(host);
 
index 9a0b1e4e405dc3bb788d603423524123b3e30e0d..369b8dee2e3d3f44dadf4379f44fee0557801f61 100644 (file)
 
 #define USDHI6_MIN_DMA 64
 
+#define USDHI6_REQ_TIMEOUT_MS 4000
+
 enum usdhi6_wait_for {
        USDHI6_WAIT_FOR_REQUEST,
        USDHI6_WAIT_FOR_CMD,
@@ -1763,7 +1765,12 @@ static int usdhi6_probe(struct platform_device *pdev)
        host            = mmc_priv(mmc);
        host->mmc       = mmc;
        host->wait      = USDHI6_WAIT_FOR_REQUEST;
-       host->timeout   = msecs_to_jiffies(4000);
+       host->timeout   = msecs_to_jiffies(USDHI6_REQ_TIMEOUT_MS);
+       /*
+        * We use a fixed timeout of 4s, hence inform the core about it. A
+        * future improvement should instead respect the cmd->busy_timeout.
+        */
+       mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS;
 
        host->pinctrl = devm_pinctrl_get(&pdev->dev);
        if (IS_ERR(host->pinctrl)) {
index e48bddd95ce6bc6e5775c0f7c34319747b72684d..ef95bce508890a38a0efad1d2443b50960e62969 100644 (file)
@@ -319,6 +319,8 @@ struct via_crdr_mmc_host {
 /* some devices need a very long delay for power to stabilize */
 #define VIA_CRDR_QUIRK_300MS_PWRDELAY  0x0001
 
+#define VIA_CMD_TIMEOUT_MS             1000
+
 static const struct pci_device_id via_ids[] = {
        {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
@@ -551,14 +553,17 @@ static void via_sdc_send_command(struct via_crdr_mmc_host *host,
 {
        void __iomem *addrbase;
        struct mmc_data *data;
+       unsigned int timeout_ms;
        u32 cmdctrl = 0;
 
        WARN_ON(host->cmd);
 
        data = cmd->data;
-       mod_timer(&host->timer, jiffies + HZ);
        host->cmd = cmd;
 
+       timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : VIA_CMD_TIMEOUT_MS;
+       mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
+
        /*Command index*/
        cmdctrl = cmd->opcode << 8;
 
index 740179f42cf211a5d97534907e2760646f79f0a2..67f917d6ecd3c9d7f58ca3148ed1c30ea1a4a63c 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/pnp.h>
 #include <linux/highmem.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 
@@ -770,22 +772,22 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
                 * interrupts.
                 */
                switch (cmd->opcode) {
-               case 11:
-               case 17:
-               case 18:
-               case 20:
-               case 24:
-               case 25:
-               case 26:
-               case 27:
-               case 30:
-               case 42:
-               case 56:
+               case SD_SWITCH_VOLTAGE:
+               case MMC_READ_SINGLE_BLOCK:
+               case MMC_READ_MULTIPLE_BLOCK:
+               case MMC_WRITE_DAT_UNTIL_STOP:
+               case MMC_WRITE_BLOCK:
+               case MMC_WRITE_MULTIPLE_BLOCK:
+               case MMC_PROGRAM_CID:
+               case MMC_PROGRAM_CSD:
+               case MMC_SEND_WRITE_PROT:
+               case MMC_LOCK_UNLOCK:
+               case MMC_GEN_CMD:
                        break;
 
                /* ACMDs. We don't keep track of state, so we just treat them
                 * like any other command. */
-               case 51:
+               case SD_APP_SEND_SCR:
                        break;
 
                default:
index 42d401ea60eea2a00192be059e4176f425fca35f..6ddab796216db9c1295555a377f09a5be8da6eea 100644 (file)
@@ -170,6 +170,16 @@ config MTD_OOPS
          buffer in a flash partition where it can be read back at some
          later point.
 
+config MTD_PSTORE
+       tristate "Log panic/oops to an MTD buffer based on pstore"
+       depends on PSTORE_BLK
+       help
+         This enables panic and oops messages to be logged to a circular
+         buffer in a flash partition where it can be read back as files after
+         mounting pstore filesystem.
+
+         If unsure, say N.
+
 config MTD_SWAP
        tristate "Swap on MTD device support"
        depends on MTD && SWAP
index 56cc60ccc477465e42d6f78949de9ed6097e5228..593d0593a038af328085c9fc4efd5fb37558454d 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_RFD_FTL)         += rfd_ftl.o
 obj-$(CONFIG_SSFDC)            += ssfdc.o
 obj-$(CONFIG_SM_FTL)           += sm_ftl.o
 obj-$(CONFIG_MTD_OOPS)         += mtdoops.o
+obj-$(CONFIG_MTD_PSTORE)       += mtdpstore.o
 obj-$(CONFIG_MTD_SWAP)         += mtdswap.o
 
 nftl-objs              := nftlcore.o nftlmount.o
diff --git a/drivers/mtd/mtdpstore.c b/drivers/mtd/mtdpstore.c
new file mode 100644 (file)
index 0000000..a4fe606
--- /dev/null
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define dev_fmt(fmt) "mtdoops-pstore: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pstore_blk.h>
+#include <linux/mtd/mtd.h>
+#include <linux/bitops.h>
+
+static struct mtdpstore_context {
+       int index;
+       struct pstore_blk_config info;
+       struct pstore_device_info dev;
+       struct mtd_info *mtd;
+       unsigned long *rmmap;           /* removed bit map */
+       unsigned long *usedmap;         /* used bit map */
+       /*
+        * used for panic write
+        * As there are no block_isbad for panic case, we should keep this
+        * status before panic to ensure panic_write not failed.
+        */
+       unsigned long *badmap;          /* bad block bit map */
+} oops_cxt;
+
+static int mtdpstore_block_isbad(struct mtdpstore_context *cxt, loff_t off)
+{
+       int ret;
+       struct mtd_info *mtd = cxt->mtd;
+       u64 blknum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       blknum = div_u64(off, mtd->erasesize);
+
+       if (test_bit(blknum, cxt->badmap))
+               return true;
+       ret = mtd_block_isbad(mtd, off);
+       if (ret < 0) {
+               dev_err(&mtd->dev, "mtd_block_isbad failed, aborting\n");
+               return ret;
+       } else if (ret > 0) {
+               set_bit(blknum, cxt->badmap);
+               return true;
+       }
+       return false;
+}
+
+static inline int mtdpstore_panic_block_isbad(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u64 blknum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       blknum = div_u64(off, mtd->erasesize);
+       return test_bit(blknum, cxt->badmap);
+}
+
+static inline void mtdpstore_mark_used(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u64 zonenum = div_u64(off, cxt->info.kmsg_size);
+
+       dev_dbg(&mtd->dev, "mark zone %llu used\n", zonenum);
+       set_bit(zonenum, cxt->usedmap);
+}
+
+static inline void mtdpstore_mark_unused(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u64 zonenum = div_u64(off, cxt->info.kmsg_size);
+
+       dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);
+       clear_bit(zonenum, cxt->usedmap);
+}
+
+static inline void mtdpstore_block_mark_unused(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
+       u64 zonenum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       zonenum = div_u64(off, cxt->info.kmsg_size);
+       while (zonecnt > 0) {
+               dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);
+               clear_bit(zonenum, cxt->usedmap);
+               zonenum++;
+               zonecnt--;
+       }
+}
+
+static inline int mtdpstore_is_used(struct mtdpstore_context *cxt, loff_t off)
+{
+       u64 zonenum = div_u64(off, cxt->info.kmsg_size);
+       u64 blknum = div_u64(off, cxt->mtd->erasesize);
+
+       if (test_bit(blknum, cxt->badmap))
+               return true;
+       return test_bit(zonenum, cxt->usedmap);
+}
+
+static int mtdpstore_block_is_used(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
+       u64 zonenum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       zonenum = div_u64(off, cxt->info.kmsg_size);
+       while (zonecnt > 0) {
+               if (test_bit(zonenum, cxt->usedmap))
+                       return true;
+               zonenum++;
+               zonecnt--;
+       }
+       return false;
+}
+
+static int mtdpstore_is_empty(struct mtdpstore_context *cxt, char *buf,
+               size_t size)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       size_t sz;
+       int i;
+
+       sz = min_t(uint32_t, size, mtd->writesize / 4);
+       for (i = 0; i < sz; i++) {
+               if (buf[i] != (char)0xFF)
+                       return false;
+       }
+       return true;
+}
+
+static void mtdpstore_mark_removed(struct mtdpstore_context *cxt, loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u64 zonenum = div_u64(off, cxt->info.kmsg_size);
+
+       dev_dbg(&mtd->dev, "mark zone %llu removed\n", zonenum);
+       set_bit(zonenum, cxt->rmmap);
+}
+
+static void mtdpstore_block_clear_removed(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
+       u64 zonenum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       zonenum = div_u64(off, cxt->info.kmsg_size);
+       while (zonecnt > 0) {
+               clear_bit(zonenum, cxt->rmmap);
+               zonenum++;
+               zonecnt--;
+       }
+}
+
+static int mtdpstore_block_is_removed(struct mtdpstore_context *cxt,
+               loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;
+       u64 zonenum;
+
+       off = ALIGN_DOWN(off, mtd->erasesize);
+       zonenum = div_u64(off, cxt->info.kmsg_size);
+       while (zonecnt > 0) {
+               if (test_bit(zonenum, cxt->rmmap))
+                       return true;
+               zonenum++;
+               zonecnt--;
+       }
+       return false;
+}
+
+static int mtdpstore_erase_do(struct mtdpstore_context *cxt, loff_t off)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       struct erase_info erase;
+       int ret;
+
+       off = ALIGN_DOWN(off, cxt->mtd->erasesize);
+       dev_dbg(&mtd->dev, "try to erase off 0x%llx\n", off);
+       erase.len = cxt->mtd->erasesize;
+       erase.addr = off;
+       ret = mtd_erase(cxt->mtd, &erase);
+       if (!ret)
+               mtdpstore_block_clear_removed(cxt, off);
+       else
+               dev_err(&mtd->dev, "erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
+                      (unsigned long long)erase.addr,
+                      (unsigned long long)erase.len, cxt->info.device);
+       return ret;
+}
+
+/*
+ * called while removing file
+ *
+ * Avoiding over erasing, do erase block only when the whole block is unused.
+ * If the block contains valid log, do erase lazily on flush_removed() when
+ * unregister.
+ */
+static ssize_t mtdpstore_erase(size_t size, loff_t off)
+{
+       struct mtdpstore_context *cxt = &oops_cxt;
+
+       if (mtdpstore_block_isbad(cxt, off))
+               return -EIO;
+
+       mtdpstore_mark_unused(cxt, off);
+
+       /* If the block still has valid data, mtdpstore do erase lazily */
+       if (likely(mtdpstore_block_is_used(cxt, off))) {
+               mtdpstore_mark_removed(cxt, off);
+               return 0;
+       }
+
+       /* all zones are unused, erase it */
+       return mtdpstore_erase_do(cxt, off);
+}
+
+/*
+ * What is security for mtdpstore?
+ * As there is no erase for panic case, we should ensure at least one zone
+ * is writable. Otherwise, panic write will fail.
+ * If zone is used, write operation will return -ENOMSG, which means that
+ * pstore/blk will try one by one until gets an empty zone. So, it is not
+ * needed to ensure the next zone is empty, but at least one.
+ */
+static int mtdpstore_security(struct mtdpstore_context *cxt, loff_t off)
+{
+       int ret = 0, i;
+       struct mtd_info *mtd = cxt->mtd;
+       u32 zonenum = (u32)div_u64(off, cxt->info.kmsg_size);
+       u32 zonecnt = (u32)div_u64(cxt->mtd->size, cxt->info.kmsg_size);
+       u32 blkcnt = (u32)div_u64(cxt->mtd->size, cxt->mtd->erasesize);
+       u32 erasesize = cxt->mtd->erasesize;
+
+       for (i = 0; i < zonecnt; i++) {
+               u32 num = (zonenum + i) % zonecnt;
+
+               /* found empty zone */
+               if (!test_bit(num, cxt->usedmap))
+                       return 0;
+       }
+
+       /* If there is no any empty zone, we have no way but to do erase */
+       while (blkcnt--) {
+               div64_u64_rem(off + erasesize, cxt->mtd->size, (u64 *)&off);
+
+               if (mtdpstore_block_isbad(cxt, off))
+                       continue;
+
+               ret = mtdpstore_erase_do(cxt, off);
+               if (!ret) {
+                       mtdpstore_block_mark_unused(cxt, off);
+                       break;
+               }
+       }
+
+       if (ret)
+               dev_err(&mtd->dev, "all blocks bad!\n");
+       dev_dbg(&mtd->dev, "end security\n");
+       return ret;
+}
+
+static ssize_t mtdpstore_write(const char *buf, size_t size, loff_t off)
+{
+       struct mtdpstore_context *cxt = &oops_cxt;
+       struct mtd_info *mtd = cxt->mtd;
+       size_t retlen;
+       int ret;
+
+       if (mtdpstore_block_isbad(cxt, off))
+               return -ENOMSG;
+
+       /* zone is used, please try next one */
+       if (mtdpstore_is_used(cxt, off))
+               return -ENOMSG;
+
+       dev_dbg(&mtd->dev, "try to write off 0x%llx size %zu\n", off, size);
+       ret = mtd_write(cxt->mtd, off, size, &retlen, (u_char *)buf);
+       if (ret < 0 || retlen != size) {
+               dev_err(&mtd->dev, "write failure at %lld (%zu of %zu written), err %d\n",
+                               off, retlen, size, ret);
+               return -EIO;
+       }
+       mtdpstore_mark_used(cxt, off);
+
+       mtdpstore_security(cxt, off);
+       return retlen;
+}
+
+static inline bool mtdpstore_is_io_error(int ret)
+{
+       return ret < 0 && !mtd_is_bitflip(ret) && !mtd_is_eccerr(ret);
+}
+
+/*
+ * All zones will be read as pstore/blk will read zone one by one when do
+ * recover.
+ */
+static ssize_t mtdpstore_read(char *buf, size_t size, loff_t off)
+{
+       struct mtdpstore_context *cxt = &oops_cxt;
+       struct mtd_info *mtd = cxt->mtd;
+       size_t retlen, done;
+       int ret;
+
+       if (mtdpstore_block_isbad(cxt, off))
+               return -ENOMSG;
+
+       dev_dbg(&mtd->dev, "try to read off 0x%llx size %zu\n", off, size);
+       for (done = 0, retlen = 0; done < size; done += retlen) {
+               retlen = 0;
+
+               ret = mtd_read(cxt->mtd, off + done, size - done, &retlen,
+                               (u_char *)buf + done);
+               if (mtdpstore_is_io_error(ret)) {
+                       dev_err(&mtd->dev, "read failure at %lld (%zu of %zu read), err %d\n",
+                                       off + done, retlen, size - done, ret);
+                       /* the zone may be broken, try next one */
+                       return -ENOMSG;
+               }
+
+               /*
+                * ECC error. The impact on log data is so small. Maybe we can
+                * still read it and try to understand. So mtdpstore just hands
+                * over what it gets and user can judge whether the data is
+                * valid or not.
+                */
+               if (mtd_is_eccerr(ret)) {
+                       dev_err(&mtd->dev, "ecc error at %lld (%zu of %zu read), err %d\n",
+                                       off + done, retlen, size - done, ret);
+                       /* driver may not set retlen when ecc error */
+                       retlen = retlen == 0 ? size - done : retlen;
+               }
+       }
+
+       if (mtdpstore_is_empty(cxt, buf, size))
+               mtdpstore_mark_unused(cxt, off);
+       else
+               mtdpstore_mark_used(cxt, off);
+
+       mtdpstore_security(cxt, off);
+       return retlen;
+}
+
+static ssize_t mtdpstore_panic_write(const char *buf, size_t size, loff_t off)
+{
+       struct mtdpstore_context *cxt = &oops_cxt;
+       struct mtd_info *mtd = cxt->mtd;
+       size_t retlen;
+       int ret;
+
+       if (mtdpstore_panic_block_isbad(cxt, off))
+               return -ENOMSG;
+
+       /* zone is used, please try next one */
+       if (mtdpstore_is_used(cxt, off))
+               return -ENOMSG;
+
+       ret = mtd_panic_write(cxt->mtd, off, size, &retlen, (u_char *)buf);
+       if (ret < 0 || size != retlen) {
+               dev_err(&mtd->dev, "panic write failure at %lld (%zu of %zu read), err %d\n",
+                               off, retlen, size, ret);
+               return -EIO;
+       }
+       mtdpstore_mark_used(cxt, off);
+
+       return retlen;
+}
+
+static void mtdpstore_notify_add(struct mtd_info *mtd)
+{
+       int ret;
+       struct mtdpstore_context *cxt = &oops_cxt;
+       struct pstore_blk_config *info = &cxt->info;
+       unsigned long longcnt;
+
+       if (!strcmp(mtd->name, info->device))
+               cxt->index = mtd->index;
+
+       if (mtd->index != cxt->index || cxt->index < 0)
+               return;
+
+       dev_dbg(&mtd->dev, "found matching MTD device %s\n", mtd->name);
+
+       if (mtd->size < info->kmsg_size * 2) {
+               dev_err(&mtd->dev, "MTD partition %d not big enough\n",
+                               mtd->index);
+               return;
+       }
+       /*
+        * kmsg_size must be aligned to 4096 Bytes, which is limited by
+        * psblk. The default value of kmsg_size is 64KB. If kmsg_size
+        * is larger than erasesize, some errors will occur since mtdpsotre
+        * is designed on it.
+        */
+       if (mtd->erasesize < info->kmsg_size) {
+               dev_err(&mtd->dev, "eraseblock size of MTD partition %d too small\n",
+                               mtd->index);
+               return;
+       }
+       if (unlikely(info->kmsg_size % mtd->writesize)) {
+               dev_err(&mtd->dev, "record size %lu KB must align to write size %d KB\n",
+                               info->kmsg_size / 1024,
+                               mtd->writesize / 1024);
+               return;
+       }
+
+       longcnt = BITS_TO_LONGS(div_u64(mtd->size, info->kmsg_size));
+       cxt->rmmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
+       cxt->usedmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
+
+       longcnt = BITS_TO_LONGS(div_u64(mtd->size, mtd->erasesize));
+       cxt->badmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);
+
+       cxt->dev.total_size = mtd->size;
+       /* just support dmesg right now */
+       cxt->dev.flags = PSTORE_FLAGS_DMESG;
+       cxt->dev.read = mtdpstore_read;
+       cxt->dev.write = mtdpstore_write;
+       cxt->dev.erase = mtdpstore_erase;
+       cxt->dev.panic_write = mtdpstore_panic_write;
+
+       ret = register_pstore_device(&cxt->dev);
+       if (ret) {
+               dev_err(&mtd->dev, "mtd%d register to psblk failed\n",
+                               mtd->index);
+               return;
+       }
+       cxt->mtd = mtd;
+       dev_info(&mtd->dev, "Attached to MTD device %d\n", mtd->index);
+}
+
+static int mtdpstore_flush_removed_do(struct mtdpstore_context *cxt,
+               loff_t off, size_t size)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       u_char *buf;
+       int ret;
+       size_t retlen;
+       struct erase_info erase;
+
+       buf = kmalloc(mtd->erasesize, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* 1st. read to cache */
+       ret = mtd_read(mtd, off, mtd->erasesize, &retlen, buf);
+       if (mtdpstore_is_io_error(ret))
+               goto free;
+
+       /* 2nd. erase block */
+       erase.len = mtd->erasesize;
+       erase.addr = off;
+       ret = mtd_erase(mtd, &erase);
+       if (ret)
+               goto free;
+
+       /* 3rd. write back */
+       while (size) {
+               unsigned int zonesize = cxt->info.kmsg_size;
+
+               /* there is valid data on block, write back */
+               if (mtdpstore_is_used(cxt, off)) {
+                       ret = mtd_write(mtd, off, zonesize, &retlen, buf);
+                       if (ret)
+                               dev_err(&mtd->dev, "write failure at %lld (%zu of %u written), err %d\n",
+                                               off, retlen, zonesize, ret);
+               }
+
+               off += zonesize;
+               size -= min_t(unsigned int, zonesize, size);
+       }
+
+free:
+       kfree(buf);
+       return ret;
+}
+
+/*
+ * What does mtdpstore_flush_removed() do?
+ * When user remove any log file on pstore filesystem, mtdpstore should do
+ * something to ensure log file removed. If the whole block is no longer used,
+ * it's nice to erase the block. However if the block still contains valid log,
+ * what mtdpstore can do is to erase and write the valid log back.
+ */
+static int mtdpstore_flush_removed(struct mtdpstore_context *cxt)
+{
+       struct mtd_info *mtd = cxt->mtd;
+       int ret;
+       loff_t off;
+       u32 blkcnt = (u32)div_u64(mtd->size, mtd->erasesize);
+
+       for (off = 0; blkcnt > 0; blkcnt--, off += mtd->erasesize) {
+               ret = mtdpstore_block_isbad(cxt, off);
+               if (ret)
+                       continue;
+
+               ret = mtdpstore_block_is_removed(cxt, off);
+               if (!ret)
+                       continue;
+
+               ret = mtdpstore_flush_removed_do(cxt, off, mtd->erasesize);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static void mtdpstore_notify_remove(struct mtd_info *mtd)
+{
+       struct mtdpstore_context *cxt = &oops_cxt;
+
+       if (mtd->index != cxt->index || cxt->index < 0)
+               return;
+
+       mtdpstore_flush_removed(cxt);
+
+       unregister_pstore_device(&cxt->dev);
+       kfree(cxt->badmap);
+       kfree(cxt->usedmap);
+       kfree(cxt->rmmap);
+       cxt->mtd = NULL;
+       cxt->index = -1;
+}
+
+static struct mtd_notifier mtdpstore_notifier = {
+       .add    = mtdpstore_notify_add,
+       .remove = mtdpstore_notify_remove,
+};
+
+static int __init mtdpstore_init(void)
+{
+       int ret;
+       struct mtdpstore_context *cxt = &oops_cxt;
+       struct pstore_blk_config *info = &cxt->info;
+
+       ret = pstore_blk_get_config(info);
+       if (unlikely(ret))
+               return ret;
+
+       if (strlen(info->device) == 0) {
+               pr_err("mtd device must be supplied (device name is empty)\n");
+               return -EINVAL;
+       }
+       if (!info->kmsg_size) {
+               pr_err("no backend enabled (kmsg_size is 0)\n");
+               return -EINVAL;
+       }
+
+       /* Setup the MTD device to use */
+       ret = kstrtoint((char *)info->device, 0, &cxt->index);
+       if (ret)
+               cxt->index = -1;
+
+       register_mtd_user(&mtdpstore_notifier);
+       return 0;
+}
+module_init(mtdpstore_init);
+
+static void __exit mtdpstore_exit(void)
+{
+       unregister_mtd_user(&mtdpstore_notifier);
+}
+module_exit(mtdpstore_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
+MODULE_DESCRIPTION("MTD backend for pstore/blk");
index b57b84fb97d0a356834a4b80e28ba1b8d71e3855..14d890b00d2cb54d380c13fcb60011884241306a 100644 (file)
@@ -1297,7 +1297,7 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,
        if (!ubi_dbg_chk_io(ubi))
                return 0;
 
-       buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+       buf1 = __vmalloc(len, GFP_NOFS);
        if (!buf1) {
                ubi_err(ubi, "cannot allocate memory to check writes");
                return 0;
@@ -1361,7 +1361,7 @@ int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
        if (!ubi_dbg_chk_io(ubi))
                return 0;
 
-       buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+       buf = __vmalloc(len, GFP_NOFS);
        if (!buf) {
                ubi_err(ubi, "cannot allocate memory to check for 0xFFs");
                return 0;
index 007481557191fee55f1157b801bcba6b3a56d69d..9b8346638f697199a5bf953475d660bcf07e1fc8 100644 (file)
@@ -149,8 +149,10 @@ int bond_sysfs_slave_add(struct slave *slave)
 
        err = kobject_init_and_add(&slave->kobj, &slave_ktype,
                                   &(slave->dev->dev.kobj), "bonding_slave");
-       if (err)
+       if (err) {
+               kobject_put(&slave->kobj);
                return err;
+       }
 
        for (a = slave_attrs; *a; ++a) {
                err = sysfs_create_file(&slave->kobj, &((*a)->attr));
index e8aae64db1caa30e65d0d4d7f13b573d4574d3c2..e113269c220a070fd38f8dd75f9101f8f51bd00e 100644 (file)
@@ -102,13 +102,17 @@ static void felix_vlan_add(struct dsa_switch *ds, int port,
                           const struct switchdev_obj_port_vlan *vlan)
 {
        struct ocelot *ocelot = ds->priv;
+       u16 flags = vlan->flags;
        u16 vid;
        int err;
 
+       if (dsa_is_cpu_port(ds, port))
+               flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
+
        for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
                err = ocelot_vlan_add(ocelot, port, vid,
-                                     vlan->flags & BRIDGE_VLAN_INFO_PVID,
-                                     vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+                                     flags & BRIDGE_VLAN_INFO_PVID,
+                                     flags & BRIDGE_VLAN_INFO_UNTAGGED);
                if (err) {
                        dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n",
                                vid, port, err);
index d1a83716d9345d3d45b4466cc78fee7192b20cf6..58e0d9a781e9a70240603e4d57af63bf928d34c9 100644 (file)
@@ -4176,14 +4176,12 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
        int i, intr_process, rc, tmo_count;
        struct input *req = msg;
        u32 *data = msg;
-       __le32 *resp_len;
        u8 *valid;
        u16 cp_ring_id, len = 0;
        struct hwrm_err_output *resp = bp->hwrm_cmd_resp_addr;
        u16 max_req_len = BNXT_HWRM_MAX_REQ_LEN;
        struct hwrm_short_input short_input = {0};
        u32 doorbell_offset = BNXT_GRCPF_REG_CHIMP_COMM_TRIGGER;
-       u8 *resp_addr = (u8 *)bp->hwrm_cmd_resp_addr;
        u32 bar_offset = BNXT_GRCPF_REG_CHIMP_COMM;
        u16 dst = BNXT_HWRM_CHNL_CHIMP;
 
@@ -4201,7 +4199,6 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                bar_offset = BNXT_GRCPF_REG_KONG_COMM;
                doorbell_offset = BNXT_GRCPF_REG_KONG_COMM_TRIGGER;
                resp = bp->hwrm_cmd_kong_resp_addr;
-               resp_addr = (u8 *)bp->hwrm_cmd_kong_resp_addr;
        }
 
        memset(resp, 0, PAGE_SIZE);
@@ -4270,7 +4267,6 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
        tmo_count = HWRM_SHORT_TIMEOUT_COUNTER;
        timeout = timeout - HWRM_SHORT_MIN_TIMEOUT * HWRM_SHORT_TIMEOUT_COUNTER;
        tmo_count += DIV_ROUND_UP(timeout, HWRM_MIN_TIMEOUT);
-       resp_len = (__le32 *)(resp_addr + HWRM_RESP_LEN_OFFSET);
 
        if (intr_process) {
                u16 seq_id = bp->hwrm_intr_seq_id;
@@ -4298,9 +4294,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                                           le16_to_cpu(req->req_type));
                        return -EBUSY;
                }
-               len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
-                     HWRM_RESP_LEN_SFT;
-               valid = resp_addr + len - 1;
+               len = le16_to_cpu(resp->resp_len);
+               valid = ((u8 *)resp) + len - 1;
        } else {
                int j;
 
@@ -4311,8 +4306,7 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                         */
                        if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))
                                return -EBUSY;
-                       len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >>
-                             HWRM_RESP_LEN_SFT;
+                       len = le16_to_cpu(resp->resp_len);
                        if (len)
                                break;
                        /* on first few passes, just barely sleep */
@@ -4334,7 +4328,7 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
                }
 
                /* Last byte of resp contains valid bit */
-               valid = resp_addr + len - 1;
+               valid = ((u8 *)resp) + len - 1;
                for (j = 0; j < HWRM_VALID_BIT_DELAY_USEC; j++) {
                        /* make sure we read from updated DMA memory */
                        dma_rmb();
@@ -9310,7 +9304,7 @@ static void __bnxt_close_nic(struct bnxt *bp, bool irq_re_init,
        bnxt_free_skbs(bp);
 
        /* Save ring stats before shutdown */
-       if (bp->bnapi)
+       if (bp->bnapi && irq_re_init)
                bnxt_get_ring_stats(bp, &bp->net_stats_prev);
        if (irq_re_init) {
                bnxt_free_irq(bp);
index f6a3250ef1c55442bc672833e663af751567afdc..3d39638521d6c47137927f0d8a1b619cb5e1a28a 100644 (file)
@@ -656,11 +656,6 @@ struct nqe_cn {
 #define HWRM_CMD_TIMEOUT               (bp->hwrm_cmd_timeout)
 #define HWRM_RESET_TIMEOUT             ((HWRM_CMD_TIMEOUT) * 4)
 #define HWRM_COREDUMP_TIMEOUT          ((HWRM_CMD_TIMEOUT) * 12)
-#define HWRM_RESP_ERR_CODE_MASK                0xffff
-#define HWRM_RESP_LEN_OFFSET           4
-#define HWRM_RESP_LEN_MASK             0xffff0000
-#define HWRM_RESP_LEN_SFT              16
-#define HWRM_RESP_VALID_MASK           0xff000000
 #define BNXT_HWRM_REQ_MAX_SIZE         128
 #define BNXT_HWRM_REQS_PER_PAGE                (BNXT_PAGE_SIZE /       \
                                         BNXT_HWRM_REQ_MAX_SIZE)
index 34046a6286e8d0985bd0e195cd1bcd33c9c42dc8..360f9a95c1d50a44bad7862de594dec108af9443 100644 (file)
@@ -2012,11 +2012,12 @@ int bnxt_flash_package_from_file(struct net_device *dev, const char *filename,
 
        bnxt_hwrm_fw_set_time(bp);
 
-       if (bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE,
-                                BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE,
-                                &index, &item_len, NULL) != 0) {
+       rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE,
+                                 BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE,
+                                 &index, &item_len, NULL);
+       if (rc) {
                netdev_err(dev, "PKG update area not created in nvram\n");
-               return -ENOBUFS;
+               return rc;
        }
 
        rc = request_firmware(&fw, filename, &dev->dev);
index 2cd1f8efdfa3a9e8aaefcee91b14eb526debac37..6bfa7575af942dca852ea31b1bf0aa766186caad 100644 (file)
@@ -2914,7 +2914,7 @@ static int dpaa_eth_probe(struct platform_device *pdev)
        }
 
        /* Do this here, so we can be verbose early */
-       SET_NETDEV_DEV(net_dev, dev);
+       SET_NETDEV_DEV(net_dev, dev->parent);
        dev_set_drvdata(dev, net_dev);
 
        priv = netdev_priv(net_dev);
index 3de549c6c6930204bbc11d18c81c9ff0ac927c75..197dc5b2c0905d26110f747efa0b3071d6457e18 100644 (file)
@@ -4678,12 +4678,10 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
                        dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc);
                        break;
                }
-               dev_info(dev, "Partner protocol version is %d\n",
-                        crq->version_exchange_rsp.version);
-               if (be16_to_cpu(crq->version_exchange_rsp.version) <
-                   ibmvnic_version)
-                       ibmvnic_version =
+               ibmvnic_version =
                            be16_to_cpu(crq->version_exchange_rsp.version);
+               dev_info(dev, "Partner protocol version is %d\n",
+                        ibmvnic_version);
                send_cap_queries(adapter);
                break;
        case QUERY_CAPABILITY_RSP:
index 177c6da80c576f06b73c7754d4a68a350379c22c..2730b1c7dddb6deb1ca13f37805d08bf8b09fa07 100644 (file)
@@ -7549,7 +7549,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        e1000_print_device_info(adapter);
 
-       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        if (pci_dev_run_wake(pdev) && hw->mac.type < e1000_pch_cnp)
                pm_runtime_put_noidle(&pdev->dev);
index b46bff8fe05686d22f5eeea187d6a0e0e97defd8..8bb3db2cbd417f908a07eb3628372ca2ab539f53 100644 (file)
@@ -3445,7 +3445,7 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
-       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        pm_runtime_put_noidle(&pdev->dev);
        return 0;
index 69fa1ce1f92719b0f3ea91fdfeda4cc514aadd30..59fc0097438f303be4dbf9d131e6e290da6b0cd3 100644 (file)
@@ -4825,7 +4825,7 @@ static int igc_probe(struct pci_dev *pdev,
        pcie_print_link_status(pdev);
        netdev_info(netdev, "MAC: %pM\n", netdev->dev_addr);
 
-       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
+       dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE);
 
        pm_runtime_put_noidle(&pdev->dev);
 
index 7d69a3061f1789d0804cff1f0c7f612ff02ed465..fd375cbe586e154907debeb402449c4f90c4d2ee 100644 (file)
@@ -80,7 +80,7 @@ config MLX5_ESWITCH
 
 config MLX5_TC_CT
        bool "MLX5 TC connection tracking offload support"
-       depends on MLX5_CORE_EN && NET_SWITCHDEV && NF_FLOW_TABLE && NET_ACT_CT && NET_TC_SKB_EXT
+       depends on MLX5_ESWITCH && NF_FLOW_TABLE && NET_ACT_CT && NET_TC_SKB_EXT
        default y
        help
          Say Y here if you want to support offloading connection tracking rules
index 59745402747be1b2de165bc3fc0297ed8a1c433a..0a5aada0f50f97882e0108beb56f3e0033ea727e 100644 (file)
@@ -1068,10 +1068,12 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv);
 
 void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len,
                                   int num_channels);
-void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params,
-                                u8 cq_period_mode);
-void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params,
-                                u8 cq_period_mode);
+
+void mlx5e_reset_tx_moderation(struct mlx5e_params *params, u8 cq_period_mode);
+void mlx5e_reset_rx_moderation(struct mlx5e_params *params, u8 cq_period_mode);
+void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode);
+void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode);
+
 void mlx5e_set_rq_type(struct mlx5_core_dev *mdev, struct mlx5e_params *params);
 void mlx5e_init_rq_type_params(struct mlx5_core_dev *mdev,
                               struct mlx5e_params *params);
index 2c4a670c8ffd4b076460b5b7d6e8b70c7499e5f3..2a8950b3056f95445bc335835c0d694fa7d01011 100644 (file)
@@ -369,17 +369,19 @@ enum mlx5e_fec_supported_link_mode {
                        *_policy = MLX5_GET(pplm_reg, _buf, fec_override_admin_##link); \
        } while (0)
 
-#define MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(buf, policy, write, link)          \
-       do {                                                                    \
-               u16 *__policy = &(policy);                                      \
-               bool _write = (write);                                          \
-                                                                               \
-               if (_write && *__policy)                                        \
-                       *__policy = find_first_bit((u_long *)__policy,          \
-                                                  sizeof(u16) * BITS_PER_BYTE);\
-               MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, *__policy, _write, link);  \
-               if (!_write && *__policy)                                       \
-                       *__policy = 1 << *__policy;                             \
+#define MLX5E_FEC_OVERRIDE_ADMIN_50G_POLICY(buf, policy, write, link)                  \
+       do {                                                                            \
+               unsigned long policy_long;                                              \
+               u16 *__policy = &(policy);                                              \
+               bool _write = (write);                                                  \
+                                                                                       \
+               policy_long = *__policy;                                                \
+               if (_write && *__policy)                                                \
+                       *__policy = find_first_bit(&policy_long,                        \
+                                                  sizeof(policy_long) * BITS_PER_BYTE);\
+               MLX5E_FEC_OVERRIDE_ADMIN_POLICY(buf, *__policy, _write, link);          \
+               if (!_write && *__policy)                                               \
+                       *__policy = 1 << *__policy;                                     \
        } while (0)
 
 /* get/set FEC admin field for a given speed */
index 6d703ddee4e27fef265f97aefff8dac422dfc8ed..bc290ae80a5311ed845b59f86c9b9b625b66a12b 100644 (file)
@@ -527,8 +527,8 @@ int mlx5e_ethtool_set_coalesce(struct mlx5e_priv *priv,
        struct dim_cq_moder *rx_moder, *tx_moder;
        struct mlx5_core_dev *mdev = priv->mdev;
        struct mlx5e_channels new_channels = {};
+       bool reset_rx, reset_tx;
        int err = 0;
-       bool reset;
 
        if (!MLX5_CAP_GEN(mdev, cq_moderation))
                return -EOPNOTSUPP;
@@ -566,15 +566,28 @@ int mlx5e_ethtool_set_coalesce(struct mlx5e_priv *priv,
        }
        /* we are opened */
 
-       reset = (!!coal->use_adaptive_rx_coalesce != priv->channels.params.rx_dim_enabled) ||
-               (!!coal->use_adaptive_tx_coalesce != priv->channels.params.tx_dim_enabled);
+       reset_rx = !!coal->use_adaptive_rx_coalesce != priv->channels.params.rx_dim_enabled;
+       reset_tx = !!coal->use_adaptive_tx_coalesce != priv->channels.params.tx_dim_enabled;
 
-       if (!reset) {
+       if (!reset_rx && !reset_tx) {
                mlx5e_set_priv_channels_coalesce(priv, coal);
                priv->channels.params = new_channels.params;
                goto out;
        }
 
+       if (reset_rx) {
+               u8 mode = MLX5E_GET_PFLAG(&new_channels.params,
+                                         MLX5E_PFLAG_RX_CQE_BASED_MODER);
+
+               mlx5e_reset_rx_moderation(&new_channels.params, mode);
+       }
+       if (reset_tx) {
+               u8 mode = MLX5E_GET_PFLAG(&new_channels.params,
+                                         MLX5E_PFLAG_TX_CQE_BASED_MODER);
+
+               mlx5e_reset_tx_moderation(&new_channels.params, mode);
+       }
+
        err = mlx5e_safe_switch_channels(priv, &new_channels, NULL, NULL);
 
 out:
@@ -665,11 +678,12 @@ static const u32 pplm_fec_2_ethtool_linkmodes[] = {
 static int get_fec_supported_advertised(struct mlx5_core_dev *dev,
                                        struct ethtool_link_ksettings *link_ksettings)
 {
-       u_long active_fec = 0;
+       unsigned long active_fec_long;
+       u32 active_fec;
        u32 bitn;
        int err;
 
-       err = mlx5e_get_fec_mode(dev, (u32 *)&active_fec, NULL);
+       err = mlx5e_get_fec_mode(dev, &active_fec, NULL);
        if (err)
                return (err == -EOPNOTSUPP) ? 0 : err;
 
@@ -682,10 +696,11 @@ static int get_fec_supported_advertised(struct mlx5_core_dev *dev,
        MLX5E_ADVERTISE_SUPPORTED_FEC(MLX5E_FEC_LLRS_272_257_1,
                                      ETHTOOL_LINK_MODE_FEC_LLRS_BIT);
 
+       active_fec_long = active_fec;
        /* active fec is a bit set, find out which bit is set and
         * advertise the corresponding ethtool bit
         */
-       bitn = find_first_bit(&active_fec, sizeof(u32) * BITS_PER_BYTE);
+       bitn = find_first_bit(&active_fec_long, sizeof(active_fec_long) * BITS_PER_BYTE);
        if (bitn < ARRAY_SIZE(pplm_fec_2_ethtool_linkmodes))
                __set_bit(pplm_fec_2_ethtool_linkmodes[bitn],
                          link_ksettings->link_modes.advertising);
@@ -1517,8 +1532,8 @@ static int mlx5e_get_fecparam(struct net_device *netdev,
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
-       u16 fec_configured = 0;
-       u32 fec_active = 0;
+       u16 fec_configured;
+       u32 fec_active;
        int err;
 
        err = mlx5e_get_fec_mode(mdev, &fec_active, &fec_configured);
@@ -1526,14 +1541,14 @@ static int mlx5e_get_fecparam(struct net_device *netdev,
        if (err)
                return err;
 
-       fecparam->active_fec = pplm2ethtool_fec((u_long)fec_active,
-                                               sizeof(u32) * BITS_PER_BYTE);
+       fecparam->active_fec = pplm2ethtool_fec((unsigned long)fec_active,
+                                               sizeof(unsigned long) * BITS_PER_BYTE);
 
        if (!fecparam->active_fec)
                return -EOPNOTSUPP;
 
-       fecparam->fec = pplm2ethtool_fec((u_long)fec_configured,
-                                        sizeof(u16) * BITS_PER_BYTE);
+       fecparam->fec = pplm2ethtool_fec((unsigned long)fec_configured,
+                                        sizeof(unsigned long) * BITS_PER_BYTE);
 
        return 0;
 }
index c6b83042d4318f26328205651256802abc93031b..bd8d0e096085716735b528c410a55ace9db77c5d 100644 (file)
@@ -4716,7 +4716,7 @@ static u8 mlx5_to_net_dim_cq_period_mode(u8 cq_period_mode)
                DIM_CQ_PERIOD_MODE_START_FROM_EQE;
 }
 
-void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
+void mlx5e_reset_tx_moderation(struct mlx5e_params *params, u8 cq_period_mode)
 {
        if (params->tx_dim_enabled) {
                u8 dim_period_mode = mlx5_to_net_dim_cq_period_mode(cq_period_mode);
@@ -4725,13 +4725,9 @@ void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
        } else {
                params->tx_cq_moderation = mlx5e_get_def_tx_moderation(cq_period_mode);
        }
-
-       MLX5E_SET_PFLAG(params, MLX5E_PFLAG_TX_CQE_BASED_MODER,
-                       params->tx_cq_moderation.cq_period_mode ==
-                               MLX5_CQ_PERIOD_MODE_START_FROM_CQE);
 }
 
-void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
+void mlx5e_reset_rx_moderation(struct mlx5e_params *params, u8 cq_period_mode)
 {
        if (params->rx_dim_enabled) {
                u8 dim_period_mode = mlx5_to_net_dim_cq_period_mode(cq_period_mode);
@@ -4740,7 +4736,19 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
        } else {
                params->rx_cq_moderation = mlx5e_get_def_rx_moderation(cq_period_mode);
        }
+}
+
+void mlx5e_set_tx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
+{
+       mlx5e_reset_tx_moderation(params, cq_period_mode);
+       MLX5E_SET_PFLAG(params, MLX5E_PFLAG_TX_CQE_BASED_MODER,
+                       params->tx_cq_moderation.cq_period_mode ==
+                               MLX5_CQ_PERIOD_MODE_START_FROM_CQE);
+}
 
+void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode)
+{
+       mlx5e_reset_rx_moderation(params, cq_period_mode);
        MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_BASED_MODER,
                        params->rx_cq_moderation.cq_period_mode ==
                                MLX5_CQ_PERIOD_MODE_START_FROM_CQE);
index 5bcf95fcdd59f006b5f7b56079ffabaf1b4ab654..10f705761666b43f5e7941f12ce57449dd66e52f 100644 (file)
@@ -2068,7 +2068,7 @@ static int mlx5e_flower_parse_meta(struct net_device *filter_dev,
        flow_rule_match_meta(rule, &match);
        if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
                NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
-               return -EINVAL;
+               return -EOPNOTSUPP;
        }
 
        ingress_dev = __dev_get_by_index(dev_net(filter_dev),
@@ -2076,13 +2076,13 @@ static int mlx5e_flower_parse_meta(struct net_device *filter_dev,
        if (!ingress_dev) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Can't find the ingress port to match on");
-               return -EINVAL;
+               return -ENOENT;
        }
 
        if (ingress_dev != filter_dev) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Can't match on the ingress filter port");
-               return -EINVAL;
+               return -EOPNOTSUPP;
        }
 
        return 0;
@@ -3849,10 +3849,6 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
                                if (!mlx5e_is_valid_eswitch_fwd_dev(priv, out_dev)) {
                                        NL_SET_ERR_MSG_MOD(extack,
                                                           "devices are not on same switch HW, can't offload forwarding");
-                                       netdev_warn(priv->netdev,
-                                                   "devices %s %s not on same switch HW, can't offload forwarding\n",
-                                                   priv->netdev->name,
-                                                   out_dev->name);
                                        return -EOPNOTSUPP;
                                }
 
@@ -4614,7 +4610,7 @@ void mlx5e_tc_stats_matchall(struct mlx5e_priv *priv,
        dpkts = cur_stats.rx_packets - rpriv->prev_vf_vport_stats.rx_packets;
        dbytes = cur_stats.rx_bytes - rpriv->prev_vf_vport_stats.rx_bytes;
        rpriv->prev_vf_vport_stats = cur_stats;
-       flow_stats_update(&ma->stats, dpkts, dbytes, jiffies,
+       flow_stats_update(&ma->stats, dbytes, dpkts, jiffies,
                          FLOW_ACTION_HW_STATS_DELAYED);
 }
 
index c1618b818f3abcd1787314ff8e12c79dc8500c95..17f818a5409038881e9344913d77be52727756ca 100644 (file)
@@ -1549,6 +1549,22 @@ static void shutdown(struct pci_dev *pdev)
        mlx5_pci_disable_device(dev);
 }
 
+static int mlx5_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+
+       mlx5_unload_one(dev, false);
+
+       return 0;
+}
+
+static int mlx5_resume(struct pci_dev *pdev)
+{
+       struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+
+       return mlx5_load_one(dev, false);
+}
+
 static const struct pci_device_id mlx5_core_pci_table[] = {
        { PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_CONNECTIB) },
        { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF},   /* Connect-IB VF */
@@ -1592,6 +1608,8 @@ static struct pci_driver mlx5_core_driver = {
        .id_table       = mlx5_core_pci_table,
        .probe          = init_one,
        .remove         = remove_one,
+       .suspend        = mlx5_suspend,
+       .resume         = mlx5_resume,
        .shutdown       = shutdown,
        .err_handler    = &mlx5_err_handler,
        .sriov_configure   = mlx5_core_sriov_configure,
index c694dbc239d0129bb22c0787c9c23fca5d501f0a..6b60771ccb195e407c90ea5f056f56f8dd03fece 100644 (file)
@@ -1440,7 +1440,8 @@ __nfp_flower_update_merge_stats(struct nfp_app *app,
                ctx_id = be32_to_cpu(sub_flow->meta.host_ctx_id);
                priv->stats[ctx_id].pkts += pkts;
                priv->stats[ctx_id].bytes += bytes;
-               max_t(u64, priv->stats[ctx_id].used, used);
+               priv->stats[ctx_id].used = max_t(u64, used,
+                                                priv->stats[ctx_id].used);
        }
 }
 
index 2a533280b1241c829a3140557520e48e744b40a5..29b9c728a65e2282c2c22a441bebce2277f7faf8 100644 (file)
@@ -3651,7 +3651,7 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
        ahw->diag_cnt = 0;
        ret = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INTRPT_TEST);
        if (ret)
-               goto fail_diag_irq;
+               goto fail_mbx_args;
 
        if (adapter->flags & QLCNIC_MSIX_ENABLED)
                intrpt_id = ahw->intr_tbl[0].id;
@@ -3681,6 +3681,8 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev)
 
 done:
        qlcnic_free_mbx_args(&cmd);
+
+fail_mbx_args:
        qlcnic_83xx_diag_free_res(netdev, drv_sds_rings);
 
 fail_diag_irq:
index 1f319c9cee468aca92198a35abb4f3035837d468..7e9cbfd235308b24bb323e87a6b5948307b8f94e 100644 (file)
@@ -630,7 +630,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                        config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                        ptp_v2 = PTP_TCR_TSVER2ENA;
                        snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
-                       ts_event_en = PTP_TCR_TSEVNTENA;
+                       if (priv->synopsys_id != DWMAC_CORE_5_10)
+                               ts_event_en = PTP_TCR_TSEVNTENA;
                        ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
                        ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
                        ptp_over_ethernet = PTP_TCR_TSIPENA;
index 53c214a22b95501b633592b3c624a532dbe202da..59f1ba4d49bc10b9aa88ce5aab4ba7537d14601e 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Software PHY emulation
  *
- * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk>
+ * Code taken from fixed_phy.c by Russell King.
  *
  * Author: Vitaly Bordug <vbordug@ru.mvista.com>
  *         Anton Vorontsov <avorontsov@ru.mvista.com>
index 4bb8552a00d3de951a49edbaff0d7b309f992703..4a2c7355be63d7336b5f47d1e4c7487349136fbb 100644 (file)
@@ -1324,6 +1324,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)},    /* Alcatel L800MA */
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
+       {QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1040, 2)}, /* Telit LE922A */
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1050, 2)}, /* Telit FN980 */
        {QMI_FIXED_INTF(0x1bc7, 0x1100, 3)},    /* Telit ME910 */
index 1f709b65c29b2bd40d5c3b8553728a4fedd014b3..59e7255150418612a5721c59103937eca1b43f21 100644 (file)
@@ -1083,10 +1083,10 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
 
        mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
 
-       dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, device);
-       dev_id_chiprev = FIELD_GET(QCA_MANUFACTURER_ID_REV_MASK, device);
+       dev_id_base = (device & 0x0F00);
+       dev_id_chiprev = (device & 0x00FF);
        switch (dev_id_base) {
-       case QCA_MANUFACTURER_ID_AR6005_BASE:
+       case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
                if (dev_id_chiprev < 4)
                        mbox_info->ext_info[0].htc_ext_sz =
                                ATH10K_HIF_MBOX0_EXT_WIDTH;
@@ -1097,7 +1097,7 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
                        mbox_info->ext_info[0].htc_ext_sz =
                                ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
                break;
-       case QCA_MANUFACTURER_ID_QCA9377_BASE:
+       case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
                mbox_info->ext_info[0].htc_ext_sz =
                        ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
                break;
@@ -2185,19 +2185,16 @@ static int ath10k_sdio_probe(struct sdio_func *func,
        skb_queue_head_init(&ar_sdio->rx_head);
        INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
 
-       dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device);
-       switch (dev_id_base) {
-       case QCA_MANUFACTURER_ID_AR6005_BASE:
-       case QCA_MANUFACTURER_ID_QCA9377_BASE:
-               ar->dev_id = QCA9377_1_0_DEVICE_ID;
-               break;
-       default:
+       dev_id_base = (id->device & 0x0F00);
+       if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
+           dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
                ret = -ENODEV;
                ath10k_err(ar, "unsupported device id %u (0x%x)\n",
                           dev_id_base, id->device);
                goto err_free_wq;
        }
 
+       ar->dev_id = QCA9377_1_0_DEVICE_ID;
        ar->id.vendor = id->vendor;
        ar->id.device = id->device;
 
@@ -2246,10 +2243,8 @@ static void ath10k_sdio_remove(struct sdio_func *func)
 }
 
 static const struct sdio_device_id ath10k_sdio_devices[] = {
-       {SDIO_DEVICE(QCA_MANUFACTURER_CODE,
-                    (QCA_SDIO_ID_AR6005_BASE | 0xA))},
-       {SDIO_DEVICE(QCA_MANUFACTURER_CODE,
-                    (QCA_SDIO_ID_QCA9377_BASE | 0x1))},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
        {},
 };
 
index 33195f49acabc478088026c96802428e8a93b414..e8951f9cdb5f3948cd137422e6edf33d757578e6 100644 (file)
 
 #define ATH10K_HIF_MBOX_BLOCK_SIZE              256
 
-#define QCA_MANUFACTURER_ID_BASE                GENMASK(11, 8)
-#define QCA_MANUFACTURER_ID_AR6005_BASE         0x5
-#define QCA_MANUFACTURER_ID_QCA9377_BASE        0x7
-#define QCA_SDIO_ID_AR6005_BASE                 0x500
-#define QCA_SDIO_ID_QCA9377_BASE                0x700
-#define QCA_MANUFACTURER_ID_REV_MASK            0x00FF
-#define QCA_MANUFACTURER_CODE                   0x271 /* Qualcomm/Atheros */
-
 #define ATH10K_SDIO_MAX_BUFFER_SIZE             4096 /*Unsure of this constant*/
 
 /* Mailbox address in SDIO address space */
index dc6bd8cd9b837d85155d494a0afd2b32b7775240..c6dafc38936ab6ef409c6f5f6db02c12c27fedb2 100644 (file)
 #define MAX_SCATTER_ENTRIES_PER_REQ      16
 #define MAX_SCATTER_REQ_TRANSFER_SIZE    (32 * 1024)
 
-#define MANUFACTURER_ID_AR6003_BASE        0x300
-#define MANUFACTURER_ID_AR6004_BASE        0x400
-    /* SDIO manufacturer ID and Codes */
-#define MANUFACTURER_ID_ATH6KL_BASE_MASK     0xFF00
-#define MANUFACTURER_CODE                  0x271       /* Atheros */
-
 /* Mailbox address in SDIO address space */
 #define HIF_MBOX_BASE_ADDR                 0x800
 #define HIF_MBOX_WIDTH                     0x800
index bb50680580f35f90d218a378d8ba066b0dc84e6f..6b51a2dceadc030dd50065624c84caadd8cd5f0b 100644 (file)
@@ -799,8 +799,7 @@ static int ath6kl_sdio_config(struct ath6kl *ar)
 
        sdio_claim_host(func);
 
-       if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
-           MANUFACTURER_ID_AR6003_BASE) {
+       if (ar_sdio->id->device >= SDIO_DEVICE_ID_ATHEROS_AR6003_00) {
                /* enable 4-bit ASYNC interrupt on AR6003 or later */
                ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
                                                CCCR_SDIO_IRQ_MODE_REG,
@@ -1409,13 +1408,13 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
 }
 
 static const struct sdio_device_id ath6kl_sdio_devices[] = {
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
-       {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x19))},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_00)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_01)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_00)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_01)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_02)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_18)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_19)},
        {},
 };
 
index 881a7938c4942cfc2e1d2948742f17cb1be18b7c..02b0cfd535abfb4a5587bdff058b11a9d4702fc1 100644 (file)
@@ -180,8 +180,8 @@ static void b43_sdio_remove(struct sdio_func *func)
 }
 
 static const struct sdio_device_id b43_sdio_ids[] = {
-       { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
-       { SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */
+       { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII) },
+       { SDIO_DEVICE(SDIO_VENDOR_ID_CGUYS, SDIO_DEVICE_ID_CGUYS_EW_CG1102GC) },
        { },
 };
 
index b684a5b6d90449e38490519def42c0e9dcd7b71c..a1fdb618cf148c1906cd2ebc9bbcbb76554f8dca 100644 (file)
@@ -970,9 +970,9 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
        BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
-       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
-       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
-       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359),
+       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373),
+       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012),
+       BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359),
        { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
index 3a08252f1a53fba378ffde9b3686d9b9e82ed3e3..1c9561665a67d5c4271b76b8e318564063dfeb19 100644 (file)
@@ -4187,7 +4187,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
                                   bus->hostintmask, NULL);
 
                switch (sdiod->func1->device) {
-               case SDIO_DEVICE_ID_CYPRESS_4373:
+               case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
                        brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
                                  CY_4373_F2_WATERMARK);
                        brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
@@ -4201,7 +4201,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
                                           CY_4373_F2_WATERMARK |
                                           SBSDIO_MESBUSYCTRL_ENAB, &err);
                        break;
-               case SDIO_DEVICE_ID_CYPRESS_43012:
+               case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012:
                        brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
                                  CY_43012_F2_WATERMARK);
                        brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
index acf61b93b7825ad645f1bd8b6e8daaf1fdcb8fba..44fbd0acb87a8922309b99ef5daaa6c4c9833e07 100644 (file)
@@ -65,7 +65,7 @@ static const struct sdio_device_id if_sdio_ids[] = {
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
                        SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
-                       SDIO_DEVICE_ID_MARVELL_8688WLAN) },
+                       SDIO_DEVICE_ID_MARVELL_8688_WLAN) },
        { /* end: all zeroes */                         },
 };
 
index 6a2dcb01caf40f9df496bd728a93f83a922f65af..a042965962a2d405a13358f6310d559e14dedc3d 100644 (file)
@@ -480,45 +480,25 @@ static void mwifiex_sdio_coredump(struct device *dev)
                schedule_work(&card->work);
 }
 
-/* Device ID for SD8786 */
-#define SDIO_DEVICE_ID_MARVELL_8786   (0x9116)
-/* Device ID for SD8787 */
-#define SDIO_DEVICE_ID_MARVELL_8787   (0x9119)
-/* Device ID for SD8797 */
-#define SDIO_DEVICE_ID_MARVELL_8797   (0x9129)
-/* Device ID for SD8897 */
-#define SDIO_DEVICE_ID_MARVELL_8897   (0x912d)
-/* Device ID for SD8887 */
-#define SDIO_DEVICE_ID_MARVELL_8887   (0x9135)
-/* Device ID for SD8801 */
-#define SDIO_DEVICE_ID_MARVELL_8801   (0x9139)
-/* Device ID for SD8977 */
-#define SDIO_DEVICE_ID_MARVELL_8977   (0x9145)
-/* Device ID for SD8987 */
-#define SDIO_DEVICE_ID_MARVELL_8987   (0x9149)
-/* Device ID for SD8997 */
-#define SDIO_DEVICE_ID_MARVELL_8997   (0x9141)
-
-
 /* WLAN IDs */
 static const struct sdio_device_id mwifiex_ids[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786_WLAN),
                .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_WLAN),
                .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_WLAN),
                .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_WLAN),
                .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_WLAN),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8887},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801_WLAN),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8977},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8987},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN),
                .driver_data = (unsigned long)&mwifiex_sdio_sd8997},
        {},
 };
index de613c623a2cf032010badf77f2ce58e5b751188..69857f0807040cba01f0401f095018c80abccddc 100644 (file)
@@ -434,15 +434,7 @@ int s3fwrn5_fw_download(struct s3fwrn5_fw_info *fw_info)
                goto out;
        }
 
-       {
-               SHASH_DESC_ON_STACK(desc, tfm);
-
-               desc->tfm = tfm;
-
-               ret = crypto_shash_digest(desc, fw->image, image_size,
-                                         hash_data);
-               shash_desc_zero(desc);
-       }
+       ret = crypto_shash_tfm_digest(tfm, fw->image, image_size, hash_data);
 
        crypto_free_shash(tfm);
        if (ret) {
index a1d69f9b2d4a469acf00503e04f88fc07e9bc528..0b9ca6d20ffadc48e36d15185f2272f23f6024fd 100644 (file)
@@ -173,8 +173,10 @@ static int st21nfca_tm_send_atr_res(struct nfc_hci_dev *hdev,
                memcpy(atr_res->gbi, atr_req->gbi, gb_len);
                r = nfc_set_remote_general_bytes(hdev->ndev, atr_res->gbi,
                                                  gb_len);
-               if (r < 0)
+               if (r < 0) {
+                       kfree_skb(skb);
                        return r;
+               }
        }
 
        info->dep_info.curr_nfc_dep_pni = 0;
index 3726dc780d15b1f3284f14eb16971d9ddf310ce6..cc46e250fcac22a075ab964a880e272b2f498433 100644 (file)
@@ -1382,16 +1382,19 @@ static void nvme_disable_admin_queue(struct nvme_dev *dev, bool shutdown)
 
 /*
  * Called only on a device that has been disabled and after all other threads
- * that can check this device's completion queues have synced. This is the
- * last chance for the driver to see a natural completion before
- * nvme_cancel_request() terminates all incomplete requests.
+ * that can check this device's completion queues have synced, except
+ * nvme_poll(). This is the last chance for the driver to see a natural
+ * completion before nvme_cancel_request() terminates all incomplete requests.
  */
 static void nvme_reap_pending_cqes(struct nvme_dev *dev)
 {
        int i;
 
-       for (i = dev->ctrl.queue_count - 1; i > 0; i--)
+       for (i = dev->ctrl.queue_count - 1; i > 0; i--) {
+               spin_lock(&dev->queues[i].cq_poll_lock);
                nvme_process_cq(&dev->queues[i]);
+               spin_unlock(&dev->queues[i].cq_poll_lock);
+       }
 }
 
 static int nvme_cmb_qdepth(struct nvme_dev *dev, int nr_io_queues,
index 12ea4a4ad60779afc551aecfffd8c2886a5688cf..6c9edc8bbc9521b2aa00b8ee1dc1187fa4d072fe 100644 (file)
@@ -113,7 +113,7 @@ static int event_buffer_open(struct inode *inode, struct file *file)
 {
        int err = -EPERM;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EPERM;
 
        if (test_and_set_bit_lock(0, &buffer_opened))
index 312cc45c44c7805da932b0e4f84bdd7273df8e55..bf779f291f1526a739422139c8bdb4d04e72a512 100644 (file)
@@ -275,7 +275,7 @@ static int pciehp_suspend(struct pcie_device *dev)
         * If the port is already runtime suspended we can keep it that
         * way.
         */
-       if (dev_pm_smart_suspend_and_suspended(&dev->port->dev))
+       if (dev_pm_skip_suspend(&dev->port->dev))
                return 0;
 
        pciehp_disable_interrupt(dev);
index 0454ca0e4e3f7d9099f1728e23f8d2ef3fc6b6f3..da6510af12214af1dac6c93edc02a9a68eba67a5 100644 (file)
@@ -776,7 +776,7 @@ static int pci_pm_suspend(struct device *dev)
 
 static int pci_pm_suspend_late(struct device *dev)
 {
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
@@ -789,10 +789,8 @@ static int pci_pm_suspend_noirq(struct device *dev)
        struct pci_dev *pci_dev = to_pci_dev(dev);
        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 
-       if (dev_pm_smart_suspend_and_suspended(dev)) {
-               dev->power.may_skip_resume = true;
+       if (dev_pm_skip_suspend(dev))
                return 0;
-       }
 
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
@@ -880,8 +878,8 @@ Fixup:
         * pci_pm_complete() to take care of fixing up the device's state
         * anyway, if need be.
         */
-       dev->power.may_skip_resume = device_may_wakeup(dev) ||
-                                       !device_can_wakeup(dev);
+       if (device_can_wakeup(dev) && !device_may_wakeup(dev))
+               dev->power.may_skip_resume = false;
 
        return 0;
 }
@@ -893,17 +891,9 @@ static int pci_pm_resume_noirq(struct device *dev)
        pci_power_t prev_state = pci_dev->current_state;
        bool skip_bus_pm = pci_dev->skip_bus_pm;
 
-       if (dev_pm_may_skip_resume(dev))
+       if (dev_pm_skip_resume(dev))
                return 0;
 
-       /*
-        * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
-        * during system suspend, so update their runtime PM status to "active"
-        * as they are going to be put into D0 shortly.
-        */
-       if (dev_pm_smart_suspend_and_suspended(dev))
-               pm_runtime_set_active(dev);
-
        /*
         * In the suspend-to-idle case, devices left in D0 during suspend will
         * stay in D0, so it is not necessary to restore or update their
@@ -928,6 +918,14 @@ static int pci_pm_resume_noirq(struct device *dev)
        return 0;
 }
 
+static int pci_pm_resume_early(struct device *dev)
+{
+       if (dev_pm_skip_resume(dev))
+               return 0;
+
+       return pm_generic_resume_early(dev);
+}
+
 static int pci_pm_resume(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -961,6 +959,7 @@ static int pci_pm_resume(struct device *dev)
 #define pci_pm_suspend_late    NULL
 #define pci_pm_suspend_noirq   NULL
 #define pci_pm_resume          NULL
+#define pci_pm_resume_early    NULL
 #define pci_pm_resume_noirq    NULL
 
 #endif /* !CONFIG_SUSPEND */
@@ -1127,7 +1126,7 @@ static int pci_pm_poweroff(struct device *dev)
 
 static int pci_pm_poweroff_late(struct device *dev)
 {
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
@@ -1140,7 +1139,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
        struct pci_dev *pci_dev = to_pci_dev(dev);
        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 
-       if (dev_pm_smart_suspend_and_suspended(dev))
+       if (dev_pm_skip_suspend(dev))
                return 0;
 
        if (pci_has_legacy_pm_support(pci_dev))
@@ -1358,6 +1357,7 @@ static const struct dev_pm_ops pci_dev_pm_ops = {
        .suspend = pci_pm_suspend,
        .suspend_late = pci_pm_suspend_late,
        .resume = pci_pm_resume,
+       .resume_early = pci_pm_resume_early,
        .freeze = pci_pm_freeze,
        .thaw = pci_pm_thaw,
        .poweroff = pci_pm_poweroff,
index 160d67c593105a420740b5ffb818acc23f475898..3acf151ae015a836917569e26af4f04f41488d33 100644 (file)
@@ -115,7 +115,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
 
        pci_save_state(dev);
 
-       dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP |
+       dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE |
                                           DPM_FLAG_SMART_SUSPEND);
 
        if (pci_bridge_d3_possible(dev)) {
index f2741c04289d9ce489564798ad1e2a970d733a03..35158cfd9c1a3c4fd5945cc3157c64a7165e08cb 100644 (file)
@@ -178,10 +178,9 @@ static int electra_cf_probe(struct platform_device *ofdev)
        struct device_node *np = ofdev->dev.of_node;
        struct electra_cf_socket   *cf;
        struct resource mem, io;
-       int status;
+       int status = -ENOMEM;
        const unsigned int *prop;
        int err;
-       struct vm_struct *area;
 
        err = of_address_to_resource(np, 0, &mem);
        if (err)
@@ -202,30 +201,19 @@ static int electra_cf_probe(struct platform_device *ofdev)
        cf->mem_phys = mem.start;
        cf->mem_size = PAGE_ALIGN(resource_size(&mem));
        cf->mem_base = ioremap(cf->mem_phys, cf->mem_size);
+       if (!cf->mem_base)
+               goto out_free_cf;
        cf->io_size = PAGE_ALIGN(resource_size(&io));
-
-       area = __get_vm_area(cf->io_size, 0, PHB_IO_BASE, PHB_IO_END);
-       if (area == NULL) {
-               status = -ENOMEM;
-               goto fail1;
-       }
-
-       cf->io_virt = (void __iomem *)(area->addr);
+       cf->io_virt = ioremap_phb(io.start, cf->io_size);
+       if (!cf->io_virt)
+               goto out_unmap_mem;
 
        cf->gpio_base = ioremap(0xfc103000, 0x1000);
+       if (!cf->gpio_base)
+               goto out_unmap_virt;
        dev_set_drvdata(device, cf);
 
-       if (!cf->mem_base || !cf->io_virt || !cf->gpio_base ||
-           (__ioremap_at(io.start, cf->io_virt, cf->io_size,
-                         pgprot_noncached(PAGE_KERNEL)) == NULL)) {
-               dev_err(device, "can't ioremap ranges\n");
-               status = -ENOMEM;
-               goto fail1;
-       }
-
-
        cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END;
-
        cf->iomem.start = (unsigned long)cf->mem_base;
        cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start);
        cf->iomem.flags = IORESOURCE_MEM;
@@ -305,14 +293,13 @@ fail1:
        if (cf->irq)
                free_irq(cf->irq, cf);
 
-       if (cf->io_virt)
-               __iounmap_at(cf->io_virt, cf->io_size);
-       if (cf->mem_base)
-               iounmap(cf->mem_base);
-       if (cf->gpio_base)
-               iounmap(cf->gpio_base);
-       if (area)
-               device_init_wakeup(&ofdev->dev, 0);
+       iounmap(cf->gpio_base);
+out_unmap_virt:
+       device_init_wakeup(&ofdev->dev, 0);
+       iounmap(cf->io_virt);
+out_unmap_mem:
+       iounmap(cf->mem_base);
+out_free_cf:
        kfree(cf);
        return status;
 
@@ -330,7 +317,7 @@ static int electra_cf_remove(struct platform_device *ofdev)
        free_irq(cf->irq, cf);
        del_timer_sync(&cf->timer);
 
-       __iounmap_at(cf->io_virt, cf->io_size);
+       iounmap(cf->io_virt);
        iounmap(cf->mem_base);
        iounmap(cf->gpio_base);
        release_mem_region(cf->mem_phys, cf->mem_size);
index 09ae8a970880fde4fbba33da9a67de541353fb17..a9261cf48293b90ad5b423477d07db1e5e6835df 100644 (file)
@@ -79,13 +79,6 @@ config FSL_IMX8_DDR_PMU
          can give information about memory throughput and other related
          events.
 
-config HISI_PMU
-       bool "HiSilicon SoC PMU"
-       depends on ARM64 && ACPI
-       help
-         Support for HiSilicon SoC uncore performance monitoring
-         unit (PMU), such as: L3C, HHA and DDRC.
-
 config QCOM_L2_PMU
        bool "Qualcomm Technologies L2-cache PMU"
        depends on ARCH_QCOM && ARM64 && ACPI
@@ -129,4 +122,6 @@ config ARM_SPE_PMU
          Extension, which provides periodic sampling of operations in
          the CPU pipeline and reports this via the perf AUX interface.
 
+source "drivers/perf/hisilicon/Kconfig"
+
 endmenu
index 70968c8c09d7f83a6cf8162c44f04d5bbf5dc528..518d0603e24f35652b601b98680f0ac5d2440be1 100644 (file)
@@ -690,10 +690,8 @@ static int dsu_pmu_device_probe(struct platform_device *pdev)
        }
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_warn(&pdev->dev, "Failed to find IRQ\n");
+       if (irq < 0)
                return -EINVAL;
-       }
 
        name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_%d",
                                PMUNAME, atomic_inc_return(&pmu_idx));
index f01a57e5a5f3502320345d582bfaf2c887f64c14..48e28ef93a7022f21258016d2c4a1cf109dc1539 100644 (file)
@@ -814,7 +814,7 @@ static int smmu_pmu_probe(struct platform_device *pdev)
        if (err) {
                dev_err(dev, "Error %d registering hotplug, PMU @%pa\n",
                        err, &res_0->start);
-               return err;
+               goto out_clear_affinity;
        }
 
        err = perf_pmu_register(&smmu_pmu->pmu, name, -1);
@@ -833,6 +833,8 @@ static int smmu_pmu_probe(struct platform_device *pdev)
 
 out_unregister:
        cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
+out_clear_affinity:
+       irq_set_affinity_hint(smmu_pmu->irq, NULL);
        return err;
 }
 
@@ -842,6 +844,7 @@ static int smmu_pmu_remove(struct platform_device *pdev)
 
        perf_pmu_unregister(&smmu_pmu->pmu);
        cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
+       irq_set_affinity_hint(smmu_pmu->irq, NULL);
 
        return 0;
 }
index b72c048525990b04ac976bdf77f7989c4409bbf0..d80f48798bceda66d18f5f44b448be0f0c79d682 100644 (file)
@@ -274,7 +274,7 @@ static u64 arm_spe_event_to_pmscr(struct perf_event *event)
        if (!attr->exclude_kernel)
                reg |= BIT(SYS_PMSCR_EL1_E1SPE_SHIFT);
 
-       if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && capable(CAP_SYS_ADMIN))
+       if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && perfmon_capable())
                reg |= BIT(SYS_PMSCR_EL1_CX_SHIFT);
 
        return reg;
@@ -700,7 +700,7 @@ static int arm_spe_pmu_event_init(struct perf_event *event)
                return -EOPNOTSUPP;
 
        reg = arm_spe_event_to_pmscr(event);
-       if (!capable(CAP_SYS_ADMIN) &&
+       if (!perfmon_capable() &&
            (reg & (BIT(SYS_PMSCR_EL1_PA_SHIFT) |
                    BIT(SYS_PMSCR_EL1_CX_SHIFT) |
                    BIT(SYS_PMSCR_EL1_PCT_SHIFT))))
@@ -1133,10 +1133,8 @@ static int arm_spe_pmu_irq_probe(struct arm_spe_pmu *spe_pmu)
        struct platform_device *pdev = spe_pmu->pdev;
        int irq = platform_get_irq(pdev, 0);
 
-       if (irq < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ (%d)\n", irq);
+       if (irq < 0)
                return -ENXIO;
-       }
 
        if (!irq_is_percpu(irq)) {
                dev_err(&pdev->dev, "expected PPI but got SPI (%d)\n", irq);
diff --git a/drivers/perf/hisilicon/Kconfig b/drivers/perf/hisilicon/Kconfig
new file mode 100644 (file)
index 0000000..c5d1b70
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config HISI_PMU
+       tristate "HiSilicon SoC PMU drivers"
+       depends on ARM64 && ACPI
+         help
+         Support for HiSilicon SoC L3 Cache performance monitor, Hydra Home
+         Agent performance monitor and DDR Controller performance monitor.
index c3a96ec2bf66f9dc6ca888503f2b2cf6b7ec9654..e8377061845ff77f325c2079ecbe3c9110fb37cd 100644 (file)
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \
+                         hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o
index 453f1c6a16ca858f2f37f2bf57a53ce4a22c0b13..15713faaa07e77eeed9dbc5333f2bf77c228d924 100644 (file)
@@ -394,8 +394,9 @@ static int hisi_ddrc_pmu_probe(struct platform_device *pdev)
        ret = perf_pmu_register(&ddrc_pmu->pmu, name, -1);
        if (ret) {
                dev_err(ddrc_pmu->dev, "DDRC PMU register failed!\n");
-               cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE,
-                                           &ddrc_pmu->node);
+               cpuhp_state_remove_instance_nocalls(
+                       CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, &ddrc_pmu->node);
+               irq_set_affinity_hint(ddrc_pmu->irq, NULL);
        }
 
        return ret;
@@ -406,8 +407,9 @@ static int hisi_ddrc_pmu_remove(struct platform_device *pdev)
        struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev);
 
        perf_pmu_unregister(&ddrc_pmu->pmu);
-       cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE,
-                                   &ddrc_pmu->node);
+       cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE,
+                                           &ddrc_pmu->node);
+       irq_set_affinity_hint(ddrc_pmu->irq, NULL);
 
        return 0;
 }
index 6a1dd72d8abbaa2d6c247934d514f1f264839b42..dcc5600788a9a271a2adaec47fa84703ec78fbfc 100644 (file)
@@ -283,7 +283,7 @@ static struct attribute *hisi_hha_pmu_events_attr[] = {
        HISI_PMU_EVENT_ATTR(rx_wbip,            0x05),
        HISI_PMU_EVENT_ATTR(rx_wtistash,        0x11),
        HISI_PMU_EVENT_ATTR(rd_ddr_64b,         0x1c),
-       HISI_PMU_EVENT_ATTR(wr_dr_64b,          0x1d),
+       HISI_PMU_EVENT_ATTR(wr_ddr_64b,         0x1d),
        HISI_PMU_EVENT_ATTR(rd_ddr_128b,        0x1e),
        HISI_PMU_EVENT_ATTR(wr_ddr_128b,        0x1f),
        HISI_PMU_EVENT_ATTR(spill_num,          0x20),
@@ -406,8 +406,9 @@ static int hisi_hha_pmu_probe(struct platform_device *pdev)
        ret = perf_pmu_register(&hha_pmu->pmu, name, -1);
        if (ret) {
                dev_err(hha_pmu->dev, "HHA PMU register failed!\n");
-               cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE,
-                                           &hha_pmu->node);
+               cpuhp_state_remove_instance_nocalls(
+                       CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, &hha_pmu->node);
+               irq_set_affinity_hint(hha_pmu->irq, NULL);
        }
 
        return ret;
@@ -418,8 +419,9 @@ static int hisi_hha_pmu_remove(struct platform_device *pdev)
        struct hisi_pmu *hha_pmu = platform_get_drvdata(pdev);
 
        perf_pmu_unregister(&hha_pmu->pmu);
-       cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE,
-                                   &hha_pmu->node);
+       cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE,
+                                           &hha_pmu->node);
+       irq_set_affinity_hint(hha_pmu->irq, NULL);
 
        return 0;
 }
index 1151e99b241cb1ef4d68f13f5a1e3e8645460718..8dd1278bec043721510093f89fc74c2adb5ba042 100644 (file)
@@ -396,8 +396,9 @@ static int hisi_l3c_pmu_probe(struct platform_device *pdev)
        ret = perf_pmu_register(&l3c_pmu->pmu, name, -1);
        if (ret) {
                dev_err(l3c_pmu->dev, "L3C PMU register failed!\n");
-               cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
-                                           &l3c_pmu->node);
+               cpuhp_state_remove_instance_nocalls(
+                       CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, &l3c_pmu->node);
+               irq_set_affinity_hint(l3c_pmu->irq, NULL);
        }
 
        return ret;
@@ -408,8 +409,9 @@ static int hisi_l3c_pmu_remove(struct platform_device *pdev)
        struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev);
 
        perf_pmu_unregister(&l3c_pmu->pmu);
-       cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
-                                   &l3c_pmu->node);
+       cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
+                                           &l3c_pmu->node);
+       irq_set_affinity_hint(l3c_pmu->irq, NULL);
 
        return 0;
 }
index 584de8f807cc404d7743e23342ee1073cd781e5e..97aff877a4e7bfc0489432620be2f504f23480b3 100644 (file)
@@ -35,6 +35,7 @@ ssize_t hisi_format_sysfs_show(struct device *dev,
 
        return sprintf(buf, "%s\n", (char *)eattr->var);
 }
+EXPORT_SYMBOL_GPL(hisi_format_sysfs_show);
 
 /*
  * PMU event attributes
@@ -48,6 +49,7 @@ ssize_t hisi_event_sysfs_show(struct device *dev,
 
        return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var);
 }
+EXPORT_SYMBOL_GPL(hisi_event_sysfs_show);
 
 /*
  * sysfs cpumask attributes. For uncore PMU, we only have a single CPU to show
@@ -59,6 +61,7 @@ ssize_t hisi_cpumask_sysfs_show(struct device *dev,
 
        return sprintf(buf, "%d\n", hisi_pmu->on_cpu);
 }
+EXPORT_SYMBOL_GPL(hisi_cpumask_sysfs_show);
 
 static bool hisi_validate_event_group(struct perf_event *event)
 {
@@ -97,6 +100,7 @@ int hisi_uncore_pmu_counter_valid(struct hisi_pmu *hisi_pmu, int idx)
 {
        return idx >= 0 && idx < hisi_pmu->num_counters;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_counter_valid);
 
 int hisi_uncore_pmu_get_event_idx(struct perf_event *event)
 {
@@ -113,6 +117,7 @@ int hisi_uncore_pmu_get_event_idx(struct perf_event *event)
 
        return idx;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_get_event_idx);
 
 static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx)
 {
@@ -173,6 +178,7 @@ int hisi_uncore_pmu_event_init(struct perf_event *event)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_init);
 
 /*
  * Set the counter to count the event that we're interested in,
@@ -220,6 +226,7 @@ void hisi_uncore_pmu_set_event_period(struct perf_event *event)
        /* Write start value to the hardware event counter */
        hisi_pmu->ops->write_counter(hisi_pmu, hwc, val);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_set_event_period);
 
 void hisi_uncore_pmu_event_update(struct perf_event *event)
 {
@@ -240,6 +247,7 @@ void hisi_uncore_pmu_event_update(struct perf_event *event)
                HISI_MAX_PERIOD(hisi_pmu->counter_bits);
        local64_add(delta, &event->count);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_update);
 
 void hisi_uncore_pmu_start(struct perf_event *event, int flags)
 {
@@ -262,6 +270,7 @@ void hisi_uncore_pmu_start(struct perf_event *event, int flags)
        hisi_uncore_pmu_enable_event(event);
        perf_event_update_userpage(event);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_start);
 
 void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
 {
@@ -278,6 +287,7 @@ void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
        hisi_uncore_pmu_event_update(event);
        hwc->state |= PERF_HES_UPTODATE;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_stop);
 
 int hisi_uncore_pmu_add(struct perf_event *event, int flags)
 {
@@ -300,6 +310,7 @@ int hisi_uncore_pmu_add(struct perf_event *event, int flags)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_add);
 
 void hisi_uncore_pmu_del(struct perf_event *event, int flags)
 {
@@ -311,12 +322,14 @@ void hisi_uncore_pmu_del(struct perf_event *event, int flags)
        perf_event_update_userpage(event);
        hisi_pmu->pmu_events.hw_events[hwc->idx] = NULL;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_del);
 
 void hisi_uncore_pmu_read(struct perf_event *event)
 {
        /* Read hardware counter and update the perf counter statistics */
        hisi_uncore_pmu_event_update(event);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_read);
 
 void hisi_uncore_pmu_enable(struct pmu *pmu)
 {
@@ -329,6 +342,7 @@ void hisi_uncore_pmu_enable(struct pmu *pmu)
 
        hisi_pmu->ops->start_counters(hisi_pmu);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_enable);
 
 void hisi_uncore_pmu_disable(struct pmu *pmu)
 {
@@ -336,6 +350,7 @@ void hisi_uncore_pmu_disable(struct pmu *pmu)
 
        hisi_pmu->ops->stop_counters(hisi_pmu);
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_disable);
 
 
 /*
@@ -414,10 +429,11 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
        hisi_pmu->on_cpu = cpu;
 
        /* Overflow interrupt also should use the same CPU */
-       WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu)));
+       WARN_ON(irq_set_affinity_hint(hisi_pmu->irq, cpumask_of(cpu)));
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_online_cpu);
 
 int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
 {
@@ -446,7 +462,10 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
        perf_pmu_migrate_context(&hisi_pmu->pmu, cpu, target);
        /* Use this CPU for event counting */
        hisi_pmu->on_cpu = target;
-       WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(target)));
+       WARN_ON(irq_set_affinity_hint(hisi_pmu->irq, cpumask_of(target)));
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(hisi_uncore_pmu_offline_cpu);
+
+MODULE_LICENSE("GPL v2");
index d13770785fb5ea1215558f12bdbfacab916dc5ca..fa51153688b462f32e4e64a71361126db69ae57e 100644 (file)
@@ -57,7 +57,7 @@ static struct ramoops_platform_data chromeos_ramoops_data = {
        .record_size    = 0x40000,
        .console_size   = 0x20000,
        .ftrace_size    = 0x20000,
-       .dump_oops      = 1,
+       .max_reason     = KMSG_DUMP_OOPS,
 };
 
 static struct platform_device chromeos_ramoops = {
index 0ad7ad8cf8e17386df81f0f6f7c30a8d2fcb52f0..fb739b242796bef2a67aeb4b2324902375c0520c 100644 (file)
@@ -78,6 +78,16 @@ config HUAWEI_WMI
          To compile this driver as a module, choose M here: the module
          will be called huawei-wmi.
 
+config INTEL_WMI_SBL_FW_UPDATE
+       tristate "Intel WMI Slim Bootloader firmware update signaling driver"
+       depends on ACPI_WMI
+       help
+         Say Y here if you want to be able to use the WMI interface to signal
+         Slim Bootloader to trigger update on next reboot.
+
+         To compile this driver as a module, choose M here: the module will
+         be called intel-wmi-sbl-fw-update.
+
 config INTEL_WMI_THUNDERBOLT
        tristate "Intel WMI thunderbolt force power driver"
        depends on ACPI_WMI
@@ -1269,7 +1279,8 @@ config INTEL_UNCORE_FREQ_CONTROL
 config INTEL_BXTWC_PMIC_TMU
        tristate "Intel BXT Whiskey Cove TMU Driver"
        depends on REGMAP
-       depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
+       depends on MFD_INTEL_PMC_BXT
+       depends on INTEL_SOC_PMIC_BXTWC
        ---help---
          Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
          This driver enables the alarm wakeup functionality in the TMU unit
@@ -1295,7 +1306,7 @@ config INTEL_MFLD_THERMAL
 
 config INTEL_MID_POWER_BUTTON
        tristate "power button driver for Intel MID platforms"
-       depends on INTEL_SCU_IPC && INPUT
+       depends on INTEL_SCU && INPUT
        help
          This driver handles the power button on the Intel MID platforms.
 
@@ -1327,14 +1338,6 @@ config INTEL_PMC_CORE
                - LTR Ignore
                - MPHY/PLL gating status (Sunrisepoint PCH only)
 
-config INTEL_PMC_IPC
-       tristate "Intel PMC IPC Driver"
-       depends on ACPI && PCI
-       ---help---
-       This driver provides support for PMC control on some Intel platforms.
-       The PMC is an ARC processor which defines IPC commands for communication
-       with other entities in the CPU.
-
 config INTEL_PUNIT_IPC
        tristate "Intel P-Unit IPC Driver"
        ---help---
@@ -1342,17 +1345,39 @@ config INTEL_PUNIT_IPC
          which is used to bridge the communications between kernel and P-Unit.
 
 config INTEL_SCU_IPC
-       bool "Intel SCU IPC Support"
-       depends on X86_INTEL_MID
-       default y
-       ---help---
-         IPC is used to bridge the communications between kernel and SCU on
-         some embedded Intel x86 platforms. This is not needed for PC-type
-         machines.
+       bool
+
+config INTEL_SCU
+       bool
+       select INTEL_SCU_IPC
+
+config INTEL_SCU_PCI
+       bool "Intel SCU PCI driver"
+       depends on PCI
+       select INTEL_SCU
+       help
+         This driver is used to bridge the communications between kernel
+         and SCU on some embedded Intel x86 platforms. It also creates
+         devices that are connected to the SoC through the SCU.
+         Platforms supported:
+           Medfield
+           Clovertrail
+           Merrifield
+           Broxton
+           Apollo Lake
+
+config INTEL_SCU_PLATFORM
+       tristate "Intel SCU platform driver"
+       depends on ACPI
+       select INTEL_SCU
+       help
+         This driver is used to bridge the communications between kernel
+         and SCU (sometimes called PMC as well). The driver currently
+         supports Intel Elkhart Lake and compatible platforms.
 
 config INTEL_SCU_IPC_UTIL
        tristate "Intel SCU IPC utility driver"
-       depends on INTEL_SCU_IPC
+       depends on INTEL_SCU
        ---help---
          The IPC Util driver provides an interface with the SCU enabling
          low level access for debug work and updating the firmware. Say
@@ -1360,7 +1385,9 @@ config INTEL_SCU_IPC_UTIL
 
 config INTEL_TELEMETRY
        tristate "Intel SoC Telemetry Driver"
-       depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
+       depends on X86_64
+       depends on MFD_INTEL_PMC_BXT
+       depends on INTEL_PUNIT_IPC
        ---help---
          This driver provides interfaces to configure and use
          telemetry for INTEL SoC from APL onwards. It is also
index 53408d9658740eb089a052f946e3ef3a5fe9d022..2b85852a1a87200f187b135234980ce5fa77fe17 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF)                += wmi-bmof.o
 # WMI drivers
 obj-$(CONFIG_ALIENWARE_WMI)            += alienware-wmi.o
 obj-$(CONFIG_HUAWEI_WMI)               += huawei-wmi.o
+obj-$(CONFIG_INTEL_WMI_SBL_FW_UPDATE)  += intel-wmi-sbl-fw-update.o
 obj-$(CONFIG_INTEL_WMI_THUNDERBOLT)    += intel-wmi-thunderbolt.o
 obj-$(CONFIG_MXM_WMI)                  += mxm-wmi.o
 obj-$(CONFIG_PEAQ_WMI)                 += peaq-wmi.o
@@ -138,9 +139,10 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL)   += intel_mid_thermal.o
 obj-$(CONFIG_INTEL_MID_POWER_BUTTON)   += intel_mid_powerbtn.o
 obj-$(CONFIG_INTEL_MRFLD_PWRBTN)       += intel_mrfld_pwrbtn.o
 obj-$(CONFIG_INTEL_PMC_CORE)           += intel_pmc_core.o intel_pmc_core_pltdrv.o
-obj-$(CONFIG_INTEL_PMC_IPC)            += intel_pmc_ipc.o
 obj-$(CONFIG_INTEL_PUNIT_IPC)          += intel_punit_ipc.o
 obj-$(CONFIG_INTEL_SCU_IPC)            += intel_scu_ipc.o
+obj-$(CONFIG_INTEL_SCU_PCI)            += intel_scu_pcidrv.o
+obj-$(CONFIG_INTEL_SCU_PLATFORM)       += intel_scu_pltdrv.o
 obj-$(CONFIG_INTEL_SCU_IPC_UTIL)       += intel_scu_ipcutil.o
 obj-$(CONFIG_INTEL_TELEMETRY)          += intel_telemetry_core.o \
                                           intel_telemetry_pltdrv.o \
index 8cc86f4e3ac132d86d1da6dc3ee5b3833287af40..4df7609b4aa99d7d451d0dbf160387dd27ca9e30 100644 (file)
@@ -827,7 +827,7 @@ MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnENBFT*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
 MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTVR46*:");
-MODULE_ALIAS("dmi:*:*Acer*:pnExtensa 5420*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnExtensa*5420*:");
 
 module_init(acerhdf_init);
 module_exit(acerhdf_exit);
index a666fbc2e73b5008ae6bbe02d24ac0f660fe5e61..0edafe687fa924813ee2c42509c3a4e2c12e59e3 100644 (file)
@@ -640,22 +640,15 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
 
 static void asus_led_exit(struct asus_laptop *asus)
 {
-       if (!IS_ERR_OR_NULL(asus->wled.led.dev))
-               led_classdev_unregister(&asus->wled.led);
-       if (!IS_ERR_OR_NULL(asus->bled.led.dev))
-               led_classdev_unregister(&asus->bled.led);
-       if (!IS_ERR_OR_NULL(asus->mled.led.dev))
-               led_classdev_unregister(&asus->mled.led);
-       if (!IS_ERR_OR_NULL(asus->tled.led.dev))
-               led_classdev_unregister(&asus->tled.led);
-       if (!IS_ERR_OR_NULL(asus->pled.led.dev))
-               led_classdev_unregister(&asus->pled.led);
-       if (!IS_ERR_OR_NULL(asus->rled.led.dev))
-               led_classdev_unregister(&asus->rled.led);
-       if (!IS_ERR_OR_NULL(asus->gled.led.dev))
-               led_classdev_unregister(&asus->gled.led);
-       if (!IS_ERR_OR_NULL(asus->kled.led.dev))
-               led_classdev_unregister(&asus->kled.led);
+       led_classdev_unregister(&asus->wled.led);
+       led_classdev_unregister(&asus->bled.led);
+       led_classdev_unregister(&asus->mled.led);
+       led_classdev_unregister(&asus->tled.led);
+       led_classdev_unregister(&asus->pled.led);
+       led_classdev_unregister(&asus->rled.led);
+       led_classdev_unregister(&asus->gled.led);
+       led_classdev_unregister(&asus->kled.led);
+
        if (asus->led_workqueue) {
                destroy_workqueue(asus->led_workqueue);
                asus->led_workqueue = NULL;
index c4404d9c1de4ff3fd1eac24991de546a04f09778..8c4d00482ef065e336beaba09969ee6a312527eb 100644 (file)
@@ -472,6 +472,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
        { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
        { KE_IGNORE, 0x6E, },  /* Low Battery notification */
        { KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */
+       { KE_IGNORE, 0x79, },  /* Charger type dectection notification */
        { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
        { KE_KEY, 0x7c, { KEY_MICMUTE } },
        { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
index bb7c529d7d16135347e472385693aeffb104d5be..877aade194979dc6c22078cb710783bd1deda95e 100644 (file)
@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_BRNDOWN_MIN             0x20
 #define NOTIFY_BRNDOWN_MAX             0x2e
 #define NOTIFY_FNLOCK_TOGGLE           0x4e
+#define NOTIFY_KBD_DOCK_CHANGE         0x75
 #define NOTIFY_KBD_BRTUP               0xc4
 #define NOTIFY_KBD_BRTDWN              0xc5
 #define NOTIFY_KBD_BRTTOGGLE           0xc7
@@ -116,6 +117,8 @@ struct bios_args {
        u32 arg0;
        u32 arg1;
        u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
+       u32 arg4;
+       u32 arg5;
 } __packed;
 
 /*
@@ -222,45 +225,6 @@ struct asus_wmi {
        struct asus_wmi_driver *driver;
 };
 
-/* Input **********************************************************************/
-
-static int asus_wmi_input_init(struct asus_wmi *asus)
-{
-       int err;
-
-       asus->inputdev = input_allocate_device();
-       if (!asus->inputdev)
-               return -ENOMEM;
-
-       asus->inputdev->name = asus->driver->input_name;
-       asus->inputdev->phys = asus->driver->input_phys;
-       asus->inputdev->id.bustype = BUS_HOST;
-       asus->inputdev->dev.parent = &asus->platform_device->dev;
-       set_bit(EV_REP, asus->inputdev->evbit);
-
-       err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
-       if (err)
-               goto err_free_dev;
-
-       err = input_register_device(asus->inputdev);
-       if (err)
-               goto err_free_dev;
-
-       return 0;
-
-err_free_dev:
-       input_free_device(asus->inputdev);
-       return err;
-}
-
-static void asus_wmi_input_exit(struct asus_wmi *asus)
-{
-       if (asus->inputdev)
-               input_unregister_device(asus->inputdev);
-
-       asus->inputdev = NULL;
-}
-
 /* WMI ************************************************************************/
 
 static int asus_wmi_evaluate_method3(u32 method_id,
@@ -309,7 +273,7 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
        struct acpi_buffer input;
        u64 phys_addr;
        u32 retval;
-       u32 status = -1;
+       u32 status;
 
        /*
         * Copy to dma capable address otherwise memory corruption occurs as
@@ -381,6 +345,53 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
        return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
 }
 
+/* Input **********************************************************************/
+
+static int asus_wmi_input_init(struct asus_wmi *asus)
+{
+       int err, result;
+
+       asus->inputdev = input_allocate_device();
+       if (!asus->inputdev)
+               return -ENOMEM;
+
+       asus->inputdev->name = asus->driver->input_name;
+       asus->inputdev->phys = asus->driver->input_phys;
+       asus->inputdev->id.bustype = BUS_HOST;
+       asus->inputdev->dev.parent = &asus->platform_device->dev;
+       set_bit(EV_REP, asus->inputdev->evbit);
+
+       err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
+       if (err)
+               goto err_free_dev;
+
+       result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_KBD_DOCK);
+       if (result >= 0) {
+               input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
+               input_report_switch(asus->inputdev, SW_TABLET_MODE, !result);
+       } else if (result != -ENODEV) {
+               pr_err("Error checking for keyboard-dock: %d\n", result);
+       }
+
+       err = input_register_device(asus->inputdev);
+       if (err)
+               goto err_free_dev;
+
+       return 0;
+
+err_free_dev:
+       input_free_device(asus->inputdev);
+       return err;
+}
+
+static void asus_wmi_input_exit(struct asus_wmi *asus)
+{
+       if (asus->inputdev)
+               input_unregister_device(asus->inputdev);
+
+       asus->inputdev = NULL;
+}
+
 /* Battery ********************************************************************/
 
 /* The battery maximum charging percentage */
@@ -675,14 +686,11 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
 
 static void asus_wmi_led_exit(struct asus_wmi *asus)
 {
-       if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
-               led_classdev_unregister(&asus->kbd_led);
-       if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
-               led_classdev_unregister(&asus->tpd_led);
-       if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
-               led_classdev_unregister(&asus->wlan_led);
-       if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
-               led_classdev_unregister(&asus->lightbar_led);
+       led_classdev_unregister(&asus->kbd_led);
+       led_classdev_unregister(&asus->tpd_led);
+       led_classdev_unregister(&asus->wlan_led);
+       led_classdev_unregister(&asus->lightbar_led);
+
        if (asus->led_workqueue)
                destroy_workqueue(asus->led_workqueue);
 }
@@ -2058,9 +2066,9 @@ static int asus_wmi_get_event_code(u32 value)
 
 static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
 {
-       int orig_code;
        unsigned int key_value = 1;
        bool autorelease = 1;
+       int result, orig_code;
 
        orig_code = code;
 
@@ -2105,6 +2113,17 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
                return;
        }
 
+       if (code == NOTIFY_KBD_DOCK_CHANGE) {
+               result = asus_wmi_get_devstate_simple(asus,
+                                                     ASUS_WMI_DEVID_KBD_DOCK);
+               if (result >= 0) {
+                       input_report_switch(asus->inputdev, SW_TABLET_MODE,
+                                           !result);
+                       input_sync(asus->inputdev);
+               }
+               return;
+       }
+
        if (asus->fan_boost_mode_available && code == NOTIFY_KBD_FBM) {
                fan_boost_mode_switch_next(asus);
                return;
index 84f4cc839cc3a9c5a113b0ff7d8ec0154d68628c..d513a59a5d473000e25d1f0ac29d5623e9a414b4 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmi.h>
 #include <linux/errno.h>
 #include <linux/cpu.h>
 #include <linux/gfp.h>
@@ -34,7 +35,7 @@
 #include "dcdbas.h"
 
 #define DRIVER_NAME            "dcdbas"
-#define DRIVER_VERSION         "5.6.0-3.3"
+#define DRIVER_VERSION         "5.6.0-3.4"
 #define DRIVER_DESCRIPTION     "Dell Systems Management Base Driver"
 
 static struct platform_device *dcdbas_pdev;
@@ -45,7 +46,7 @@ static unsigned long smi_data_buf_size;
 static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
 static u32 smi_data_buf_phys_addr;
 static DEFINE_MUTEX(smi_data_lock);
-static u8 *eps_buffer;
+static u8 *bios_buffer;
 
 static unsigned int host_control_action;
 static unsigned int host_control_smi_type;
@@ -518,8 +519,10 @@ static inline struct smm_eps_table *check_eps_table(u8 *addr)
 
 static int dcdbas_check_wsmt(void)
 {
+       const struct dmi_device *dev = NULL;
        struct acpi_table_wsmt *wsmt = NULL;
        struct smm_eps_table *eps = NULL;
+       u64 bios_buf_paddr;
        u64 remap_size;
        u8 *addr;
 
@@ -532,6 +535,17 @@ static int dcdbas_check_wsmt(void)
            !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
                return 0;
 
+       /*
+        * BIOS could provide the address/size of the protected buffer
+        * in an SMBIOS string or in an EPS structure in 0xFxxxx.
+        */
+
+       /* Check SMBIOS for buffer address */
+       while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev)))
+               if (sscanf(dev->name, "30[%16llx;%8llx]", &bios_buf_paddr,
+                   &remap_size) == 2)
+                       goto remap;
+
        /* Scan for EPS (entry point structure) */
        for (addr = (u8 *)__va(0xf0000);
             addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
@@ -542,34 +556,37 @@ static int dcdbas_check_wsmt(void)
        }
 
        if (!eps) {
-               dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n");
+               dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no firmware buffer found\n");
                return -ENODEV;
        }
+       bios_buf_paddr = eps->smm_comm_buff_addr;
+       remap_size = eps->num_of_4k_pages * PAGE_SIZE;
 
+remap:
        /*
         * Get physical address of buffer and map to virtual address.
         * Table gives size in 4K pages, regardless of actual system page size.
         */
-       if (upper_32_bits(eps->smm_comm_buff_addr + 8)) {
-               dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n");
+       if (upper_32_bits(bios_buf_paddr + 8)) {
+               dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
                return -EINVAL;
        }
        /*
         * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8
         * bytes are used for a semaphore, not the data buffer itself).
         */
-       remap_size = eps->num_of_4k_pages * PAGE_SIZE;
        if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
                remap_size = MAX_SMI_DATA_BUF_SIZE + 8;
-       eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB);
-       if (!eps_buffer) {
-               dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n");
+
+       bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
+       if (!bios_buffer) {
+               dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
                return -ENOMEM;
        }
 
        /* First 8 bytes is for a semaphore, not part of the smi_data_buf */
-       smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8;
-       smi_data_buf = eps_buffer + 8;
+       smi_data_buf_phys_addr = bios_buf_paddr + 8;
+       smi_data_buf = bios_buffer + 8;
        smi_data_buf_size = remap_size - 8;
        max_smi_data_buf_size = smi_data_buf_size;
        wsmt_enabled = true;
@@ -736,8 +753,8 @@ static void __exit dcdbas_exit(void)
         */
        if (dcdbas_pdev)
                smi_data_buf_free();
-       if (eps_buffer)
-               memunmap(eps_buffer);
+       if (bios_buffer)
+               memunmap(bios_buffer);
        platform_device_unregister(dcdbas_pdev_reg);
        platform_driver_unregister(&dcdbas_driver);
 }
index f8d3e3bd1bb5d5438ca4eb327106afadf0e43fab..5e9c2296931c9a750c7030add75168919ca1e6f1 100644 (file)
@@ -2204,10 +2204,13 @@ static int __init dell_init(void)
 
        dell_laptop_register_notifier(&dell_laptop_notifier);
 
-       micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
-       ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
-       if (ret < 0)
-               goto fail_led;
+       if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
+           dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
+               micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
+               ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
+               if (ret < 0)
+                       goto fail_led;
+       }
 
        if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                return 0;
index 86e8dd6a8b33f41f4c9af61c9e32d749c2549d97..c25a4286d7665b7a820f35ca652d79f2483e20f2 100644 (file)
@@ -310,6 +310,16 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = {
        /* Battery inserted */
        { KE_IGNORE, 0xfff1, { KEY_RESERVED } },
 
+       /*
+        * Detachable keyboard detached / undocked
+        * Note SW_TABLET_MODE is already reported through the intel_vbtn
+        * driver for this, so we ignore it.
+        */
+       { KE_IGNORE, 0xfff2, { KEY_RESERVED } },
+
+       /* Detachable keyboard attached / docked */
+       { KE_IGNORE, 0xfff3, { KEY_RESERVED } },
+
        /* Keyboard backlight level changed */
        { KE_IGNORE, KBD_LED_OFF_TOKEN,      { KEY_RESERVED } },
        { KE_IGNORE, KBD_LED_ON_TOKEN,       { KEY_RESERVED } },
index 776868d5e45805f69f2d92bec9daa992971b94d1..ba08c9235f769652fff9d7ee9a8ce73905aa15a2 100644 (file)
@@ -541,13 +541,11 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
 
 static void eeepc_led_exit(struct eeepc_laptop *eeepc)
 {
-       if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
-               led_classdev_unregister(&eeepc->tpd_led);
+       led_classdev_unregister(&eeepc->tpd_led);
        if (eeepc->led_workqueue)
                destroy_workqueue(eeepc->led_workqueue);
 }
 
-
 /*
  * PCI hotplug (for wlan rfkill)
  */
index a881b709af25633274690daea073b13cdc61609c..1762f335bac923c14e8229d821b36ef104b85fb1 100644 (file)
@@ -111,10 +111,10 @@ enum hp_wireless2_bits {
        HPWMI_POWER_SOFT        = 0x02,
        HPWMI_POWER_BIOS        = 0x04,
        HPWMI_POWER_HARD        = 0x08,
+       HPWMI_POWER_FW_OR_HW    = HPWMI_POWER_BIOS | HPWMI_POWER_HARD,
 };
 
-#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
-                        != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
+#define IS_HWBLOCKED(x) ((x & HPWMI_POWER_FW_OR_HW) != HPWMI_POWER_FW_OR_HW)
 #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
 
 struct bios_rfkill2_device_state {
@@ -461,8 +461,14 @@ static ssize_t postcode_show(struct device *dev, struct device_attribute *attr,
 static ssize_t als_store(struct device *dev, struct device_attribute *attr,
                         const char *buf, size_t count)
 {
-       u32 tmp = simple_strtoul(buf, NULL, 10);
-       int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
+       u32 tmp;
+       int ret;
+
+       ret = kstrtou32(buf, 10, &tmp);
+       if (ret)
+               return ret;
+
+       ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_WRITE, &tmp,
                                       sizeof(tmp), sizeof(tmp));
        if (ret)
                return ret < 0 ? ret : -EINVAL;
@@ -473,22 +479,20 @@ static ssize_t als_store(struct device *dev, struct device_attribute *attr,
 static ssize_t postcode_store(struct device *dev, struct device_attribute *attr,
                              const char *buf, size_t count)
 {
-       long unsigned int tmp2;
+       u32 tmp = 1;
+       bool clear;
        int ret;
-       u32 tmp;
 
-       ret = kstrtoul(buf, 10, &tmp2);
-       if (!ret && tmp2 != 1)
-               ret = -EINVAL;
+       ret = kstrtobool(buf, &clear);
        if (ret)
-               goto out;
+               return ret;
+
+       if (clear == false)
+               return -EINVAL;
 
        /* Clear the POST error code. It is kept until until cleared. */
-       tmp = (u32) tmp2;
        ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, HPWMI_WRITE, &tmp,
                                       sizeof(tmp), sizeof(tmp));
-
-out:
        if (ret)
                return ret < 0 ? ret : -EINVAL;
 
index cc7dd4d87cce7e96add5fe401eb64cefaccbca32..9ee79b74311c109a7912c812550c1af08cf454a2 100644 (file)
@@ -79,6 +79,13 @@ static const struct dmi_system_id button_array_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Wacom MobileStudio Pro 16"),
                },
        },
+       {
+               .ident = "HP Spectre x2 (2015)",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Spectre x2 Detachable"),
+               },
+       },
        { }
 };
 
index b5880936d78535d6462057366964bbe17ba872f4..0487b606a2749f100c9b3146ba5e7cc6ad9484d2 100644 (file)
@@ -40,28 +40,70 @@ static const struct key_entry intel_vbtn_keymap[] = {
        { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },        /* volume-down key release */
        { KE_KEY,    0xC8, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key press */
        { KE_KEY,    0xC9, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key release */
+};
+
+static const struct key_entry intel_vbtn_switchmap[] = {
        { KE_SW,     0xCA, { .sw = { SW_DOCK, 1 } } },          /* Docked */
        { KE_SW,     0xCB, { .sw = { SW_DOCK, 0 } } },          /* Undocked */
        { KE_SW,     0xCC, { .sw = { SW_TABLET_MODE, 1 } } },   /* Tablet */
        { KE_SW,     0xCD, { .sw = { SW_TABLET_MODE, 0 } } },   /* Laptop */
-       { KE_END },
 };
 
+#define KEYMAP_LEN \
+       (ARRAY_SIZE(intel_vbtn_keymap) + ARRAY_SIZE(intel_vbtn_switchmap) + 1)
+
 struct intel_vbtn_priv {
+       struct key_entry keymap[KEYMAP_LEN];
        struct input_dev *input_dev;
+       bool has_buttons;
+       bool has_switches;
        bool wakeup_mode;
 };
 
+static void detect_tablet_mode(struct platform_device *device)
+{
+       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+       acpi_handle handle = ACPI_HANDLE(&device->dev);
+       unsigned long long vgbs;
+       acpi_status status;
+       int m;
+
+       status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
+       if (ACPI_FAILURE(status))
+               return;
+
+       m = !(vgbs & TABLET_MODE_FLAG);
+       input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
+       m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0;
+       input_report_switch(priv->input_dev, SW_DOCK, m);
+}
+
 static int intel_vbtn_input_setup(struct platform_device *device)
 {
        struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
-       int ret;
+       int ret, keymap_len = 0;
+
+       if (priv->has_buttons) {
+               memcpy(&priv->keymap[keymap_len], intel_vbtn_keymap,
+                      ARRAY_SIZE(intel_vbtn_keymap) *
+                      sizeof(struct key_entry));
+               keymap_len += ARRAY_SIZE(intel_vbtn_keymap);
+       }
+
+       if (priv->has_switches) {
+               memcpy(&priv->keymap[keymap_len], intel_vbtn_switchmap,
+                      ARRAY_SIZE(intel_vbtn_switchmap) *
+                      sizeof(struct key_entry));
+               keymap_len += ARRAY_SIZE(intel_vbtn_switchmap);
+       }
+
+       priv->keymap[keymap_len].type = KE_END;
 
        priv->input_dev = devm_input_allocate_device(&device->dev);
        if (!priv->input_dev)
                return -ENOMEM;
 
-       ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
+       ret = sparse_keymap_setup(priv->input_dev, priv->keymap, NULL);
        if (ret)
                return ret;
 
@@ -69,6 +111,9 @@ static int intel_vbtn_input_setup(struct platform_device *device)
        priv->input_dev->name = "Intel Virtual Button driver";
        priv->input_dev->id.bustype = BUS_HOST;
 
+       if (priv->has_switches)
+               detect_tablet_mode(device);
+
        return input_register_device(priv->input_dev);
 }
 
@@ -114,44 +159,46 @@ out_unknown:
        dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
 }
 
-static void detect_tablet_mode(struct platform_device *device)
+static bool intel_vbtn_has_buttons(acpi_handle handle)
 {
-       const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
-       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
-       acpi_handle handle = ACPI_HANDLE(&device->dev);
-       struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
        acpi_status status;
-       int m;
 
-       if (!(chassis_type && strcmp(chassis_type, "31") == 0))
-               goto out;
+       status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
+       return ACPI_SUCCESS(status);
+}
 
-       status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
-       if (ACPI_FAILURE(status))
-               goto out;
+static bool intel_vbtn_has_switches(acpi_handle handle)
+{
+       const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
+       unsigned long long vgbs;
+       acpi_status status;
 
-       obj = vgbs_output.pointer;
-       if (!(obj && obj->type == ACPI_TYPE_INTEGER))
-               goto out;
+       /*
+        * Some normal laptops have a VGBS method despite being non-convertible
+        * and their VGBS method always returns 0, causing detect_tablet_mode()
+        * to report SW_TABLET_MODE=1 to userspace, which causes issues.
+        * These laptops have a DMI chassis_type of 9 ("Laptop"), do not report
+        * switches on any devices with a DMI chassis_type of 9.
+        */
+       if (chassis_type && strcmp(chassis_type, "9") == 0)
+               return false;
 
-       m = !(obj->integer.value & TABLET_MODE_FLAG);
-       input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
-       m = (obj->integer.value & DOCK_MODE_FLAG) ? 1 : 0;
-       input_report_switch(priv->input_dev, SW_DOCK, m);
-out:
-       kfree(vgbs_output.pointer);
+       status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
+       return ACPI_SUCCESS(status);
 }
 
 static int intel_vbtn_probe(struct platform_device *device)
 {
        acpi_handle handle = ACPI_HANDLE(&device->dev);
+       bool has_buttons, has_switches;
        struct intel_vbtn_priv *priv;
        acpi_status status;
        int err;
 
-       status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
-       if (ACPI_FAILURE(status)) {
+       has_buttons = intel_vbtn_has_buttons(handle);
+       has_switches = intel_vbtn_has_switches(handle);
+
+       if (!has_buttons && !has_switches) {
                dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
                return -ENODEV;
        }
@@ -161,14 +208,15 @@ static int intel_vbtn_probe(struct platform_device *device)
                return -ENOMEM;
        dev_set_drvdata(&device->dev, priv);
 
+       priv->has_buttons = has_buttons;
+       priv->has_switches = has_switches;
+
        err = intel_vbtn_input_setup(device);
        if (err) {
                pr_err("Failed to setup Intel Virtual Button\n");
                return err;
        }
 
-       detect_tablet_mode(device);
-
        status = acpi_install_notify_handler(handle,
                                             ACPI_DEVICE_NOTIFY,
                                             notify_handler,
diff --git a/drivers/platform/x86/intel-wmi-sbl-fw-update.c b/drivers/platform/x86/intel-wmi-sbl-fw-update.c
new file mode 100644 (file)
index 0000000..ea87fa0
--- /dev/null
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Slim Bootloader(SBL) firmware update signaling driver
+ *
+ * Slim Bootloader is a small, open-source, non UEFI compliant, boot firmware
+ * optimized for running on certain Intel platforms.
+ *
+ * SBL exposes an ACPI-WMI device via /sys/bus/wmi/devices/<INTEL_WMI_SBL_GUID>.
+ * This driver further adds "firmware_update_request" device attribute.
+ * This attribute normally has a value of 0 and userspace can signal SBL
+ * to update firmware, on next reboot, by writing a value of 1.
+ *
+ * More details of SBL firmware update process is available at:
+ * https://slimbootloader.github.io/security/firmware-update.html
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/wmi.h>
+
+#define INTEL_WMI_SBL_GUID  "44FADEB1-B204-40F2-8581-394BBDC1B651"
+
+static int get_fwu_request(struct device *dev, u32 *out)
+{
+       struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *obj;
+       acpi_status status;
+
+       status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "wmi_query_block failed\n");
+               return -ENODEV;
+       }
+
+       obj = (union acpi_object *)result.pointer;
+       if (!obj || obj->type != ACPI_TYPE_INTEGER) {
+               dev_warn(dev, "wmi_query_block returned invalid value\n");
+               kfree(obj);
+               return -EINVAL;
+       }
+
+       *out = obj->integer.value;
+       kfree(obj);
+
+       return 0;
+}
+
+static int set_fwu_request(struct device *dev, u32 in)
+{
+       struct acpi_buffer input;
+       acpi_status status;
+       u32 value;
+
+       value = in;
+       input.length = sizeof(u32);
+       input.pointer = &value;
+
+       status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "wmi_set_block failed\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static ssize_t firmware_update_request_show(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       u32 val;
+       int ret;
+
+       ret = get_fwu_request(dev, &val);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t firmware_update_request_store(struct device *dev,
+                                            struct device_attribute *attr,
+                                            const char *buf, size_t count)
+{
+       unsigned int val;
+       int ret;
+
+       ret = kstrtouint(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       /* May later be extended to support values other than 0 and 1 */
+       if (val > 1)
+               return -ERANGE;
+
+       ret = set_fwu_request(dev, val);
+       if (ret)
+               return ret;
+
+       return count;
+}
+static DEVICE_ATTR_RW(firmware_update_request);
+
+static struct attribute *firmware_update_attrs[] = {
+       &dev_attr_firmware_update_request.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(firmware_update);
+
+static int intel_wmi_sbl_fw_update_probe(struct wmi_device *wdev,
+                                        const void *context)
+{
+       dev_info(&wdev->dev, "Slim Bootloader signaling driver attached\n");
+       return 0;
+}
+
+static int intel_wmi_sbl_fw_update_remove(struct wmi_device *wdev)
+{
+       dev_info(&wdev->dev, "Slim Bootloader signaling driver removed\n");
+       return 0;
+}
+
+static const struct wmi_device_id intel_wmi_sbl_id_table[] = {
+       { .guid_string = INTEL_WMI_SBL_GUID },
+       {}
+};
+MODULE_DEVICE_TABLE(wmi, intel_wmi_sbl_id_table);
+
+static struct wmi_driver intel_wmi_sbl_fw_update_driver = {
+       .driver = {
+               .name = "intel-wmi-sbl-fw-update",
+               .dev_groups = firmware_update_groups,
+       },
+       .probe = intel_wmi_sbl_fw_update_probe,
+       .remove = intel_wmi_sbl_fw_update_remove,
+       .id_table = intel_wmi_sbl_id_table,
+};
+module_wmi_driver(intel_wmi_sbl_fw_update_driver);
+
+MODULE_AUTHOR("Jithu Joseph <jithu.joseph@intel.com>");
+MODULE_DESCRIPTION("Slim Bootloader firmware update signaling driver");
+MODULE_LICENSE("GPL v2");
index 04138215956bf80967bc70466287f2658b2413e8..48638d1c56e550eb57356c114af3ede8642112bc 100644 (file)
@@ -6,14 +6,14 @@
  *
  * Some Intel Cherry Trail based device which ship with Windows 10, have
  * this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
- * resources, for 4 different chips attached to various i2c busses:
- * 1. The Whiskey Cove pmic, which is also described by the INT34D3 ACPI device
+ * resources, for 4 different chips attached to various I²C buses:
+ * 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
  * 2. Maxim MAX17047 Fuel Gauge Controller
  * 3. FUSB302 USB Type-C Controller
  * 4. PI3USB30532 USB switch
  *
  * So this driver is a stub / pseudo driver whose only purpose is to
- * instantiate i2c-clients for chips 2 - 4, so that standard i2c drivers
+ * instantiate I²C clients for chips 2 - 4, so that standard I²C drivers
  * for these chips can bind to the them.
  */
 
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/usb/pd.h>
 
 #include "intel_cht_int33fe_common.h"
 
-enum {
-       INT33FE_NODE_FUSB302,
-       INT33FE_NODE_MAX17047,
-       INT33FE_NODE_PI3USB30532,
-       INT33FE_NODE_DISPLAYPORT,
-       INT33FE_NODE_USB_CONNECTOR,
-       INT33FE_NODE_MAX,
-};
-
 /*
- * Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates
+ * Grrr, I severely dislike buggy BIOS-es. At least one BIOS enumerates
  * the max17047 both through the INT33FE ACPI device (it is right there
  * in the resources table) as well as through a separate MAX17047 device.
  *
- * These helpers are used to work around this by checking if an i2c-client
+ * These helpers are used to work around this by checking if an I²C client
  * for the max17047 has already been registered.
  */
 static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
 {
        struct i2c_client **max17047 = data;
        struct acpi_device *adev;
-       const char *hid;
 
        adev = ACPI_COMPANION(dev);
        if (!adev)
                return 0;
 
-       hid = acpi_device_hid(adev);
-
        /* The MAX17047 ACPI node doesn't have an UID, so we don't check that */
-       if (strcmp(hid, "MAX17047"))
+       if (!acpi_dev_hid_uid_match(adev, "MAX17047", NULL))
                return 0;
 
        *max17047 = to_i2c_client(dev);
@@ -66,11 +55,16 @@ static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
 
 static const char * const max17047_suppliers[] = { "bq24190-charger" };
 
-static const struct property_entry max17047_props[] = {
+static const struct property_entry max17047_properties[] = {
        PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
        { }
 };
 
+static const struct software_node max17047_node = {
+       .name = "max17047",
+       .properties = max17047_properties,
+};
+
 /*
  * We are not using inline property here because those are constant,
  * and we need to adjust this one at runtime to point to real
@@ -80,12 +74,17 @@ static struct software_node_ref_args fusb302_mux_refs[] = {
        { .node = NULL },
 };
 
-static const struct property_entry fusb302_props[] = {
+static const struct property_entry fusb302_properties[] = {
        PROPERTY_ENTRY_STRING("linux,extcon-name", "cht_wcove_pwrsrc"),
        PROPERTY_ENTRY_REF_ARRAY("usb-role-switch", fusb302_mux_refs),
        { }
 };
 
+static const struct software_node fusb302_node = {
+       .name = "fusb302",
+       .properties = fusb302_properties,
+};
+
 #define PDO_FIXED_FLAGS \
        (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
 
@@ -98,31 +97,40 @@ static const u32 snk_pdo[] = {
        PDO_VAR(5000, 12000, 3000),
 };
 
-static const struct software_node nodes[];
+static const struct software_node pi3usb30532_node = {
+       .name = "pi3usb30532",
+};
+
+static const struct software_node displayport_node = {
+       .name = "displayport",
+};
 
-static const struct property_entry usb_connector_props[] = {
+static const struct property_entry usb_connector_properties[] = {
        PROPERTY_ENTRY_STRING("data-role", "dual"),
        PROPERTY_ENTRY_STRING("power-role", "dual"),
        PROPERTY_ENTRY_STRING("try-power-role", "sink"),
        PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo),
        PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo),
        PROPERTY_ENTRY_U32("op-sink-microwatt", 2500000),
-       PROPERTY_ENTRY_REF("orientation-switch",
-                          &nodes[INT33FE_NODE_PI3USB30532]),
-       PROPERTY_ENTRY_REF("mode-switch",
-                          &nodes[INT33FE_NODE_PI3USB30532]),
-       PROPERTY_ENTRY_REF("displayport",
-                          &nodes[INT33FE_NODE_DISPLAYPORT]),
+       PROPERTY_ENTRY_REF("orientation-switch", &pi3usb30532_node),
+       PROPERTY_ENTRY_REF("mode-switch", &pi3usb30532_node),
+       PROPERTY_ENTRY_REF("displayport", &displayport_node),
        { }
 };
 
-static const struct software_node nodes[] = {
-       { "fusb302", NULL, fusb302_props },
-       { "max17047", NULL, max17047_props },
-       { "pi3usb30532" },
-       { "displayport" },
-       { "connector", &nodes[0], usb_connector_props },
-       { }
+static const struct software_node usb_connector_node = {
+       .name = "connector",
+       .parent = &fusb302_node,
+       .properties = usb_connector_properties,
+};
+
+static const struct software_node *node_group[] = {
+       &fusb302_node,
+       &max17047_node,
+       &pi3usb30532_node,
+       &displayport_node,
+       &usb_connector_node,
+       NULL
 };
 
 static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
@@ -130,7 +138,7 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
        struct fwnode_handle *fwnode;
        struct pci_dev *pdev;
 
-       fwnode = software_node_fwnode(&nodes[INT33FE_NODE_DISPLAYPORT]);
+       fwnode = software_node_fwnode(&displayport_node);
        if (!fwnode)
                return -ENODEV;
 
@@ -155,11 +163,10 @@ static int cht_int33fe_setup_dp(struct cht_int33fe_data *data)
 
 static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data)
 {
-       software_node_unregister_nodes(nodes);
+       software_node_unregister_node_group(node_group);
 
        if (fusb302_mux_refs[0].node) {
-               fwnode_handle_put(
-                       software_node_fwnode(fusb302_mux_refs[0].node));
+               fwnode_handle_put(software_node_fwnode(fusb302_mux_refs[0].node));
                fusb302_mux_refs[0].node = NULL;
        }
 
@@ -192,7 +199,7 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data)
         */
        fusb302_mux_refs[0].node = mux_ref_node;
 
-       ret = software_node_register_nodes(nodes);
+       ret = software_node_register_node_group(node_group);
        if (ret)
                return ret;
 
@@ -222,16 +229,15 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
        struct fwnode_handle *fwnode;
        int ret;
 
-       fwnode = software_node_fwnode(&nodes[INT33FE_NODE_MAX17047]);
+       fwnode = software_node_fwnode(&max17047_node);
        if (!fwnode)
                return -ENODEV;
 
        i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
        if (max17047) {
-               /* Pre-existing i2c-client for the max17047, add device-props */
-               fwnode->secondary = ERR_PTR(-ENODEV);
-               max17047->dev.fwnode->secondary = fwnode;
-               /* And re-probe to get the new device-props applied. */
+               /* Pre-existing I²C client for the max17047, add device properties */
+               set_secondary_fwnode(&max17047->dev, fwnode);
+               /* And re-probe to get the new device properties applied */
                ret = device_reprobe(&max17047->dev);
                if (ret)
                        dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
@@ -266,7 +272,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
         *    must be registered before the fusb302 is instantiated, otherwise
         *    it will end up with a dummy-regulator.
         * Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data
-        * which is defined in i2c-cht-wc.c from where the bq24292i i2c-client
+        * which is defined in i2c-cht-wc.c from where the bq24292i I²C client
         * gets instantiated. We use regulator_get_optional here so that we
         * don't end up getting a dummy-regulator ourselves.
         */
@@ -277,7 +283,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
        }
        regulator_put(regulator);
 
-       /* The FUSB302 uses the irq at index 1 and is the only irq user */
+       /* The FUSB302 uses the IRQ at index 1 and is the only IRQ user */
        fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
        if (fusb302_irq < 0) {
                if (fusb302_irq != -EPROBE_DEFER)
@@ -289,12 +295,12 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
        if (ret)
                return ret;
 
-       /* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047 */
+       /* Work around BIOS bug, see comment on cht_int33fe_check_for_max17047() */
        ret = cht_int33fe_register_max17047(dev, data);
        if (ret)
                goto out_remove_nodes;
 
-       fwnode = software_node_fwnode(&nodes[INT33FE_NODE_FUSB302]);
+       fwnode = software_node_fwnode(&fusb302_node);
        if (!fwnode) {
                ret = -ENODEV;
                goto out_unregister_max17047;
@@ -312,7 +318,7 @@ int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
                goto out_unregister_max17047;
        }
 
-       fwnode = software_node_fwnode(&nodes[INT33FE_NODE_PI3USB30532]);
+       fwnode = software_node_fwnode(&pi3usb30532_node);
        if (!fwnode) {
                ret = -ENODEV;
                goto out_unregister_fusb302;
index 9c9f209c8a334b35f7d450bbd5f7695f265b8768..df434abbb66f18c20a9920314c33eace4a403a72 100644 (file)
@@ -46,6 +46,7 @@ struct mid_pb_ddata {
        unsigned short mirqlvl1_addr;
        unsigned short pbstat_addr;
        u8 pbstat_mask;
+       struct intel_scu_ipc_dev *scu;
        int (*setup)(struct mid_pb_ddata *ddata);
 };
 
@@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
        int ret;
        u8 pbstat;
 
-       ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
+       ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
+                                       &pbstat);
        if (ret)
                return ret;
 
@@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
 
 static int mid_irq_ack(struct mid_pb_ddata *ddata)
 {
-       return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
+       return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
+                                       MSIC_PWRBTNM);
 }
 
 static int mrfld_setup(struct mid_pb_ddata *ddata)
 {
        /* Unmask the PBIRQ and MPBIRQ on Tangier */
-       intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
-       intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
+       intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
+       intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
 
        return 0;
 }
@@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
                        return error;
        }
 
+       ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+       if (!ddata->scu)
+               return -EPROBE_DEFER;
+
        error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
                                          IRQF_ONESHOT, DRIVER_NAME, ddata);
        if (error) {
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
deleted file mode 100644 (file)
index 2433bf7..0000000
+++ /dev/null
@@ -1,949 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Driver for the Intel PMC IPC mechanism
- *
- * (C) Copyright 2014-2015 Intel Corporation
- *
- * This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by
- *     Sreedhara DS <sreedhara.ds@intel.com>
- *
- * PMC running in ARC processor communicates with other entity running in IA
- * core through IPC mechanism which in turn messaging between IA core ad PMC.
- */
-
-#include <linux/acpi.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/io-64-nonatomic-lo-hi.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-
-#include <asm/intel_pmc_ipc.h>
-
-#include <linux/platform_data/itco_wdt.h>
-
-/*
- * IPC registers
- * The IA write to IPC_CMD command register triggers an interrupt to the ARC,
- * The ARC handles the interrupt and services it, writing optional data to
- * the IPC1 registers, updates the IPC_STS response register with the status.
- */
-#define IPC_CMD                        0x00
-#define                IPC_CMD_MSI             BIT(8)
-#define                IPC_CMD_SIZE            16
-#define                IPC_CMD_SUBCMD          12
-#define IPC_STATUS             0x04
-#define                IPC_STATUS_IRQ          BIT(2)
-#define                IPC_STATUS_ERR          BIT(1)
-#define                IPC_STATUS_BUSY         BIT(0)
-#define IPC_SPTR               0x08
-#define IPC_DPTR               0x0C
-#define IPC_WRITE_BUFFER       0x80
-#define IPC_READ_BUFFER                0x90
-
-/* Residency with clock rate at 19.2MHz to usecs */
-#define S0IX_RESIDENCY_IN_USECS(d, s)          \
-({                                             \
-       u64 result = 10ull * ((d) + (s));       \
-       do_div(result, 192);                    \
-       result;                                 \
-})
-
-/*
- * 16-byte buffer for sending data associated with IPC command.
- */
-#define IPC_DATA_BUFFER_SIZE   16
-
-#define IPC_LOOP_CNT           3000000
-#define IPC_MAX_SEC            3
-
-#define IPC_TRIGGER_MODE_IRQ           true
-
-/* exported resources from IFWI */
-#define PLAT_RESOURCE_IPC_INDEX                0
-#define PLAT_RESOURCE_IPC_SIZE         0x1000
-#define PLAT_RESOURCE_GCR_OFFSET       0x1000
-#define PLAT_RESOURCE_GCR_SIZE         0x1000
-#define PLAT_RESOURCE_BIOS_DATA_INDEX  1
-#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
-#define PLAT_RESOURCE_TELEM_SSRAM_INDEX        3
-#define PLAT_RESOURCE_ISP_DATA_INDEX   4
-#define PLAT_RESOURCE_ISP_IFACE_INDEX  5
-#define PLAT_RESOURCE_GTD_DATA_INDEX   6
-#define PLAT_RESOURCE_GTD_IFACE_INDEX  7
-#define PLAT_RESOURCE_ACPI_IO_INDEX    0
-
-/*
- * BIOS does not create an ACPI device for each PMC function,
- * but exports multiple resources from one ACPI device(IPC) for
- * multiple functions. This driver is responsible to create a
- * platform device and to export resources for those functions.
- */
-#define TCO_DEVICE_NAME                        "iTCO_wdt"
-#define SMI_EN_OFFSET                  0x40
-#define SMI_EN_SIZE                    4
-#define TCO_BASE_OFFSET                        0x60
-#define TCO_REGS_SIZE                  16
-#define PUNIT_DEVICE_NAME              "intel_punit_ipc"
-#define TELEMETRY_DEVICE_NAME          "intel_telemetry"
-#define TELEM_SSRAM_SIZE               240
-#define TELEM_PMC_SSRAM_OFFSET         0x1B00
-#define TELEM_PUNIT_SSRAM_OFFSET       0x1A00
-#define TCO_PMC_OFFSET                 0x08
-#define TCO_PMC_SIZE                   0x04
-
-/* PMC register bit definitions */
-
-/* PMC_CFG_REG bit masks */
-#define PMC_CFG_NO_REBOOT_MASK         BIT_MASK(4)
-#define PMC_CFG_NO_REBOOT_EN           (1 << 4)
-#define PMC_CFG_NO_REBOOT_DIS          (0 << 4)
-
-static struct intel_pmc_ipc_dev {
-       struct device *dev;
-       void __iomem *ipc_base;
-       bool irq_mode;
-       int irq;
-       int cmd;
-       struct completion cmd_complete;
-
-       /* The following PMC BARs share the same ACPI device with the IPC */
-       resource_size_t acpi_io_base;
-       int acpi_io_size;
-       struct platform_device *tco_dev;
-
-       /* gcr */
-       void __iomem *gcr_mem_base;
-       bool has_gcr_regs;
-       spinlock_t gcr_lock;
-
-       /* punit */
-       struct platform_device *punit_dev;
-       unsigned int punit_res_count;
-
-       /* Telemetry */
-       resource_size_t telem_pmc_ssram_base;
-       resource_size_t telem_punit_ssram_base;
-       int telem_pmc_ssram_size;
-       int telem_punit_ssram_size;
-       u8 telem_res_inval;
-       struct platform_device *telemetry_dev;
-} ipcdev;
-
-static char *ipc_err_sources[] = {
-       [IPC_ERR_NONE] =
-               "no error",
-       [IPC_ERR_CMD_NOT_SUPPORTED] =
-               "command not supported",
-       [IPC_ERR_CMD_NOT_SERVICED] =
-               "command not serviced",
-       [IPC_ERR_UNABLE_TO_SERVICE] =
-               "unable to service",
-       [IPC_ERR_CMD_INVALID] =
-               "command invalid",
-       [IPC_ERR_CMD_FAILED] =
-               "command failed",
-       [IPC_ERR_EMSECURITY] =
-               "Invalid Battery",
-       [IPC_ERR_UNSIGNEDKERNEL] =
-               "Unsigned kernel",
-};
-
-/* Prevent concurrent calls to the PMC */
-static DEFINE_MUTEX(ipclock);
-
-static inline void ipc_send_command(u32 cmd)
-{
-       ipcdev.cmd = cmd;
-       if (ipcdev.irq_mode) {
-               reinit_completion(&ipcdev.cmd_complete);
-               cmd |= IPC_CMD_MSI;
-       }
-       writel(cmd, ipcdev.ipc_base + IPC_CMD);
-}
-
-static inline u32 ipc_read_status(void)
-{
-       return readl(ipcdev.ipc_base + IPC_STATUS);
-}
-
-static inline void ipc_data_writel(u32 data, u32 offset)
-{
-       writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
-}
-
-static inline u32 ipc_data_readl(u32 offset)
-{
-       return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
-}
-
-static inline u64 gcr_data_readq(u32 offset)
-{
-       return readq(ipcdev.gcr_mem_base + offset);
-}
-
-static inline int is_gcr_valid(u32 offset)
-{
-       if (!ipcdev.has_gcr_regs)
-               return -EACCES;
-
-       if (offset > PLAT_RESOURCE_GCR_SIZE)
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
- * @offset:    offset of GCR register from GCR address base
- * @data:      data pointer for storing the register output
- *
- * Reads the 64-bit PMC GCR register at given offset.
- *
- * Return:     negative value on error or 0 on success.
- */
-int intel_pmc_gcr_read64(u32 offset, u64 *data)
-{
-       int ret;
-
-       spin_lock(&ipcdev.gcr_lock);
-
-       ret = is_gcr_valid(offset);
-       if (ret < 0) {
-               spin_unlock(&ipcdev.gcr_lock);
-               return ret;
-       }
-
-       *data = readq(ipcdev.gcr_mem_base + offset);
-
-       spin_unlock(&ipcdev.gcr_lock);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
-
-/**
- * intel_pmc_gcr_update() - Update PMC GCR register bits
- * @offset:    offset of GCR register from GCR address base
- * @mask:      bit mask for update operation
- * @val:       update value
- *
- * Updates the bits of given GCR register as specified by
- * @mask and @val.
- *
- * Return:     negative value on error or 0 on success.
- */
-static int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
-{
-       u32 new_val;
-       int ret = 0;
-
-       spin_lock(&ipcdev.gcr_lock);
-
-       ret = is_gcr_valid(offset);
-       if (ret < 0)
-               goto gcr_ipc_unlock;
-
-       new_val = readl(ipcdev.gcr_mem_base + offset);
-
-       new_val &= ~mask;
-       new_val |= val & mask;
-
-       writel(new_val, ipcdev.gcr_mem_base + offset);
-
-       new_val = readl(ipcdev.gcr_mem_base + offset);
-
-       /* check whether the bit update is successful */
-       if ((new_val & mask) != (val & mask)) {
-               ret = -EIO;
-               goto gcr_ipc_unlock;
-       }
-
-gcr_ipc_unlock:
-       spin_unlock(&ipcdev.gcr_lock);
-       return ret;
-}
-
-static int update_no_reboot_bit(void *priv, bool set)
-{
-       u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
-
-       return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
-                                   PMC_CFG_NO_REBOOT_MASK, value);
-}
-
-static int intel_pmc_ipc_check_status(void)
-{
-       int status;
-       int ret = 0;
-
-       if (ipcdev.irq_mode) {
-               if (0 == wait_for_completion_timeout(
-                               &ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
-                       ret = -ETIMEDOUT;
-       } else {
-               int loop_count = IPC_LOOP_CNT;
-
-               while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
-                       udelay(1);
-               if (loop_count == 0)
-                       ret = -ETIMEDOUT;
-       }
-
-       status = ipc_read_status();
-       if (ret == -ETIMEDOUT) {
-               dev_err(ipcdev.dev,
-                       "IPC timed out, TS=0x%x, CMD=0x%x\n",
-                       status, ipcdev.cmd);
-               return ret;
-       }
-
-       if (status & IPC_STATUS_ERR) {
-               int i;
-
-               ret = -EIO;
-               i = (status >> IPC_CMD_SIZE) & 0xFF;
-               if (i < ARRAY_SIZE(ipc_err_sources))
-                       dev_err(ipcdev.dev,
-                               "IPC failed: %s, STS=0x%x, CMD=0x%x\n",
-                               ipc_err_sources[i], status, ipcdev.cmd);
-               else
-                       dev_err(ipcdev.dev,
-                               "IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
-                               status, ipcdev.cmd);
-               if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
-                       ret = -EACCES;
-       }
-
-       return ret;
-}
-
-/**
- * intel_pmc_ipc_simple_command() - Simple IPC command
- * @cmd:       IPC command code.
- * @sub:       IPC command sub type.
- *
- * Send a simple IPC command to PMC when don't need to specify
- * input/output data and source/dest pointers.
- *
- * Return:     an IPC error code or 0 on success.
- */
-static int intel_pmc_ipc_simple_command(int cmd, int sub)
-{
-       int ret;
-
-       mutex_lock(&ipclock);
-       if (ipcdev.dev == NULL) {
-               mutex_unlock(&ipclock);
-               return -ENODEV;
-       }
-       ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
-       ret = intel_pmc_ipc_check_status();
-       mutex_unlock(&ipclock);
-
-       return ret;
-}
-
-/**
- * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
- * @cmd:       IPC command code.
- * @sub:       IPC command sub type.
- * @in:                input data of this IPC command.
- * @inlen:     input data length in bytes.
- * @out:       output data of this IPC command.
- * @outlen:    output data length in dwords.
- * @sptr:      data writing to SPTR register.
- * @dptr:      data writing to DPTR register.
- *
- * Send an IPC command to PMC with input/output data and source/dest pointers.
- *
- * Return:     an IPC error code or 0 on success.
- */
-static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
-                                u32 outlen, u32 dptr, u32 sptr)
-{
-       u32 wbuf[4] = { 0 };
-       int ret;
-       int i;
-
-       if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
-               return -EINVAL;
-
-       mutex_lock(&ipclock);
-       if (ipcdev.dev == NULL) {
-               mutex_unlock(&ipclock);
-               return -ENODEV;
-       }
-       memcpy(wbuf, in, inlen);
-       writel(dptr, ipcdev.ipc_base + IPC_DPTR);
-       writel(sptr, ipcdev.ipc_base + IPC_SPTR);
-       /* The input data register is 32bit register and inlen is in Byte */
-       for (i = 0; i < ((inlen + 3) / 4); i++)
-               ipc_data_writel(wbuf[i], 4 * i);
-       ipc_send_command((inlen << IPC_CMD_SIZE) |
-                       (sub << IPC_CMD_SUBCMD) | cmd);
-       ret = intel_pmc_ipc_check_status();
-       if (!ret) {
-               /* out is read from 32bit register and outlen is in 32bit */
-               for (i = 0; i < outlen; i++)
-                       *out++ = ipc_data_readl(4 * i);
-       }
-       mutex_unlock(&ipclock);
-
-       return ret;
-}
-
-/**
- * intel_pmc_ipc_command() -  IPC command with input/output data
- * @cmd:       IPC command code.
- * @sub:       IPC command sub type.
- * @in:                input data of this IPC command.
- * @inlen:     input data length in bytes.
- * @out:       output data of this IPC command.
- * @outlen:    output data length in dwords.
- *
- * Send an IPC command to PMC with input/output data.
- *
- * Return:     an IPC error code or 0 on success.
- */
-int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
-                         u32 *out, u32 outlen)
-{
-       return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
-}
-EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
-
-static irqreturn_t ioc(int irq, void *dev_id)
-{
-       int status;
-
-       if (ipcdev.irq_mode) {
-               status = ipc_read_status();
-               writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
-       }
-       complete(&ipcdev.cmd_complete);
-
-       return IRQ_HANDLED;
-}
-
-static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-       struct intel_pmc_ipc_dev *pmc = &ipcdev;
-       int ret;
-
-       /* Only one PMC is supported */
-       if (pmc->dev)
-               return -EBUSY;
-
-       pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
-
-       spin_lock_init(&ipcdev.gcr_lock);
-
-       ret = pcim_enable_device(pdev);
-       if (ret)
-               return ret;
-
-       ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
-       if (ret)
-               return ret;
-
-       init_completion(&pmc->cmd_complete);
-
-       pmc->ipc_base = pcim_iomap_table(pdev)[0];
-
-       ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
-                               pmc);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to request irq\n");
-               return ret;
-       }
-
-       pmc->dev = &pdev->dev;
-
-       pci_set_drvdata(pdev, pmc);
-
-       return 0;
-}
-
-static const struct pci_device_id ipc_pci_ids[] = {
-       {PCI_VDEVICE(INTEL, 0x0a94), 0},
-       {PCI_VDEVICE(INTEL, 0x1a94), 0},
-       {PCI_VDEVICE(INTEL, 0x5a94), 0},
-       { 0,}
-};
-MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
-
-static struct pci_driver ipc_pci_driver = {
-       .name = "intel_pmc_ipc",
-       .id_table = ipc_pci_ids,
-       .probe = ipc_pci_probe,
-};
-
-static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
-                                             struct device_attribute *attr,
-                                             const char *buf, size_t count)
-{
-       int subcmd;
-       int cmd;
-       int ret;
-
-       ret = sscanf(buf, "%d %d", &cmd, &subcmd);
-       if (ret != 2) {
-               dev_err(dev, "Error args\n");
-               return -EINVAL;
-       }
-
-       ret = intel_pmc_ipc_simple_command(cmd, subcmd);
-       if (ret) {
-               dev_err(dev, "command %d error with %d\n", cmd, ret);
-               return ret;
-       }
-       return (ssize_t)count;
-}
-static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store);
-
-static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
-                                            struct device_attribute *attr,
-                                            const char *buf, size_t count)
-{
-       unsigned long val;
-       int subcmd;
-       int ret;
-
-       ret = kstrtoul(buf, 0, &val);
-       if (ret)
-               return ret;
-
-       if (val)
-               subcmd = 1;
-       else
-               subcmd = 0;
-       ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
-       if (ret) {
-               dev_err(dev, "command north %d error with %d\n", subcmd, ret);
-               return ret;
-       }
-       return (ssize_t)count;
-}
-static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store);
-
-static struct attribute *intel_ipc_attrs[] = {
-       &dev_attr_northpeak.attr,
-       &dev_attr_simplecmd.attr,
-       NULL
-};
-
-static const struct attribute_group intel_ipc_group = {
-       .attrs = intel_ipc_attrs,
-};
-
-static const struct attribute_group *intel_ipc_groups[] = {
-       &intel_ipc_group,
-       NULL
-};
-
-static struct resource punit_res_array[] = {
-       /* Punit BIOS */
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       /* Punit ISP */
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       /* Punit GTD */
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .flags = IORESOURCE_MEM,
-       },
-};
-
-#define TCO_RESOURCE_ACPI_IO           0
-#define TCO_RESOURCE_SMI_EN_IO         1
-#define TCO_RESOURCE_GCR_MEM           2
-static struct resource tco_res[] = {
-       /* ACPI - TCO */
-       {
-               .flags = IORESOURCE_IO,
-       },
-       /* ACPI - SMI */
-       {
-               .flags = IORESOURCE_IO,
-       },
-};
-
-static struct itco_wdt_platform_data tco_info = {
-       .name = "Apollo Lake SoC",
-       .version = 5,
-       .no_reboot_priv = &ipcdev,
-       .update_no_reboot_bit = update_no_reboot_bit,
-};
-
-#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
-#define TELEMETRY_RESOURCE_PMC_SSRAM   1
-static struct resource telemetry_res[] = {
-       /*Telemetry*/
-       {
-               .flags = IORESOURCE_MEM,
-       },
-       {
-               .flags = IORESOURCE_MEM,
-       },
-};
-
-static int ipc_create_punit_device(void)
-{
-       struct platform_device *pdev;
-       const struct platform_device_info pdevinfo = {
-               .parent = ipcdev.dev,
-               .name = PUNIT_DEVICE_NAME,
-               .id = -1,
-               .res = punit_res_array,
-               .num_res = ipcdev.punit_res_count,
-               };
-
-       pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       ipcdev.punit_dev = pdev;
-
-       return 0;
-}
-
-static int ipc_create_tco_device(void)
-{
-       struct platform_device *pdev;
-       struct resource *res;
-       const struct platform_device_info pdevinfo = {
-               .parent = ipcdev.dev,
-               .name = TCO_DEVICE_NAME,
-               .id = -1,
-               .res = tco_res,
-               .num_res = ARRAY_SIZE(tco_res),
-               .data = &tco_info,
-               .size_data = sizeof(tco_info),
-               };
-
-       res = tco_res + TCO_RESOURCE_ACPI_IO;
-       res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
-       res->end = res->start + TCO_REGS_SIZE - 1;
-
-       res = tco_res + TCO_RESOURCE_SMI_EN_IO;
-       res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
-       res->end = res->start + SMI_EN_SIZE - 1;
-
-       pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       ipcdev.tco_dev = pdev;
-
-       return 0;
-}
-
-static int ipc_create_telemetry_device(void)
-{
-       struct platform_device *pdev;
-       struct resource *res;
-       const struct platform_device_info pdevinfo = {
-               .parent = ipcdev.dev,
-               .name = TELEMETRY_DEVICE_NAME,
-               .id = -1,
-               .res = telemetry_res,
-               .num_res = ARRAY_SIZE(telemetry_res),
-               };
-
-       res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
-       res->start = ipcdev.telem_punit_ssram_base;
-       res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
-
-       res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
-       res->start = ipcdev.telem_pmc_ssram_base;
-       res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
-
-       pdev = platform_device_register_full(&pdevinfo);
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       ipcdev.telemetry_dev = pdev;
-
-       return 0;
-}
-
-static int ipc_create_pmc_devices(void)
-{
-       int ret;
-
-       /* If we have ACPI based watchdog use that instead */
-       if (!acpi_has_watchdog()) {
-               ret = ipc_create_tco_device();
-               if (ret) {
-                       dev_err(ipcdev.dev, "Failed to add tco platform device\n");
-                       return ret;
-               }
-       }
-
-       ret = ipc_create_punit_device();
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-               platform_device_unregister(ipcdev.tco_dev);
-               return ret;
-       }
-
-       if (!ipcdev.telem_res_inval) {
-               ret = ipc_create_telemetry_device();
-               if (ret) {
-                       dev_warn(ipcdev.dev,
-                               "Failed to add telemetry platform device\n");
-                       platform_device_unregister(ipcdev.punit_dev);
-                       platform_device_unregister(ipcdev.tco_dev);
-               }
-       }
-
-       return ret;
-}
-
-static int ipc_plat_get_res(struct platform_device *pdev)
-{
-       struct resource *res, *punit_res = punit_res_array;
-       void __iomem *addr;
-       int size;
-
-       res = platform_get_resource(pdev, IORESOURCE_IO,
-                                   PLAT_RESOURCE_ACPI_IO_INDEX);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get io resource\n");
-               return -ENXIO;
-       }
-       size = resource_size(res);
-       ipcdev.acpi_io_base = res->start;
-       ipcdev.acpi_io_size = size;
-       dev_info(&pdev->dev, "io res: %pR\n", res);
-
-       ipcdev.punit_res_count = 0;
-
-       /* This is index 0 to cover BIOS data register */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_BIOS_DATA_INDEX);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
-               return -ENXIO;
-       }
-       punit_res[ipcdev.punit_res_count++] = *res;
-       dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
-
-       /* This is index 1 to cover BIOS interface register */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_BIOS_IFACE_INDEX);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
-               return -ENXIO;
-       }
-       punit_res[ipcdev.punit_res_count++] = *res;
-       dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
-
-       /* This is index 2 to cover ISP data register, optional */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_ISP_DATA_INDEX);
-       if (res) {
-               punit_res[ipcdev.punit_res_count++] = *res;
-               dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
-       }
-
-       /* This is index 3 to cover ISP interface register, optional */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_ISP_IFACE_INDEX);
-       if (res) {
-               punit_res[ipcdev.punit_res_count++] = *res;
-               dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
-       }
-
-       /* This is index 4 to cover GTD data register, optional */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_GTD_DATA_INDEX);
-       if (res) {
-               punit_res[ipcdev.punit_res_count++] = *res;
-               dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
-       }
-
-       /* This is index 5 to cover GTD interface register, optional */
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_GTD_IFACE_INDEX);
-       if (res) {
-               punit_res[ipcdev.punit_res_count++] = *res;
-               dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
-       }
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_IPC_INDEX);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get ipc resource\n");
-               return -ENXIO;
-       }
-       size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
-       res->end = res->start + size - 1;
-
-       addr = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(addr))
-               return PTR_ERR(addr);
-
-       ipcdev.ipc_base = addr;
-
-       ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
-       dev_info(&pdev->dev, "ipc res: %pR\n", res);
-
-       ipcdev.telem_res_inval = 0;
-       res = platform_get_resource(pdev, IORESOURCE_MEM,
-                                   PLAT_RESOURCE_TELEM_SSRAM_INDEX);
-       if (!res) {
-               dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
-               ipcdev.telem_res_inval = 1;
-       } else {
-               ipcdev.telem_punit_ssram_base = res->start +
-                                               TELEM_PUNIT_SSRAM_OFFSET;
-               ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
-               ipcdev.telem_pmc_ssram_base = res->start +
-                                               TELEM_PMC_SSRAM_OFFSET;
-               ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
-               dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
-       }
-
-       return 0;
-}
-
-/**
- * intel_pmc_s0ix_counter_read() - Read S0ix residency.
- * @data: Out param that contains current S0ix residency count.
- *
- * Return: an error code or 0 on success.
- */
-int intel_pmc_s0ix_counter_read(u64 *data)
-{
-       u64 deep, shlw;
-
-       if (!ipcdev.has_gcr_regs)
-               return -EACCES;
-
-       deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
-       shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
-
-       *data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id ipc_acpi_ids[] = {
-       { "INT34D2", 0},
-       { }
-};
-MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
-#endif
-
-static int ipc_plat_probe(struct platform_device *pdev)
-{
-       int ret;
-
-       ipcdev.dev = &pdev->dev;
-       ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
-       init_completion(&ipcdev.cmd_complete);
-       spin_lock_init(&ipcdev.gcr_lock);
-
-       ipcdev.irq = platform_get_irq(pdev, 0);
-       if (ipcdev.irq < 0)
-               return -EINVAL;
-
-       ret = ipc_plat_get_res(pdev);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to request resource\n");
-               return ret;
-       }
-
-       ret = ipc_create_pmc_devices();
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to create pmc devices\n");
-               return ret;
-       }
-
-       if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
-                            "intel_pmc_ipc", &ipcdev)) {
-               dev_err(&pdev->dev, "Failed to request irq\n");
-               ret = -EBUSY;
-               goto err_irq;
-       }
-
-       ipcdev.has_gcr_regs = true;
-
-       return 0;
-
-err_irq:
-       platform_device_unregister(ipcdev.tco_dev);
-       platform_device_unregister(ipcdev.punit_dev);
-       platform_device_unregister(ipcdev.telemetry_dev);
-
-       return ret;
-}
-
-static int ipc_plat_remove(struct platform_device *pdev)
-{
-       devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
-       platform_device_unregister(ipcdev.tco_dev);
-       platform_device_unregister(ipcdev.punit_dev);
-       platform_device_unregister(ipcdev.telemetry_dev);
-       ipcdev.dev = NULL;
-       return 0;
-}
-
-static struct platform_driver ipc_plat_driver = {
-       .remove = ipc_plat_remove,
-       .probe = ipc_plat_probe,
-       .driver = {
-               .name = "pmc-ipc-plat",
-               .acpi_match_table = ACPI_PTR(ipc_acpi_ids),
-               .dev_groups = intel_ipc_groups,
-       },
-};
-
-static int __init intel_pmc_ipc_init(void)
-{
-       int ret;
-
-       ret = platform_driver_register(&ipc_plat_driver);
-       if (ret) {
-               pr_err("Failed to register PMC ipc platform driver\n");
-               return ret;
-       }
-       ret = pci_register_driver(&ipc_pci_driver);
-       if (ret) {
-               pr_err("Failed to register PMC ipc pci driver\n");
-               platform_driver_unregister(&ipc_plat_driver);
-               return ret;
-       }
-       return ret;
-}
-
-static void __exit intel_pmc_ipc_exit(void)
-{
-       pci_unregister_driver(&ipc_pci_driver);
-       platform_driver_unregister(&ipc_plat_driver);
-}
-
-MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
-MODULE_DESCRIPTION("Intel PMC IPC driver");
-MODULE_LICENSE("GPL v2");
-
-/* Some modules are dependent on this, so init earlier */
-fs_initcall(intel_pmc_ipc_init);
-module_exit(intel_pmc_ipc_exit);
index 3d7da526613682348238f4f5ae97499aec7e765e..d9cf7f7602b0b4e186b6ebf193e88ea416366941 100644 (file)
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/pm.h>
-#include <linux/sfi.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
 
-#include <asm/intel-mid.h>
 #include <asm/intel_scu_ipc.h>
 
 /* IPC defines the following message types */
 #define IPC_IOC                  0x100         /* IPC command register IOC bit */
 
 struct intel_scu_ipc_dev {
-       struct device *dev;
+       struct device dev;
+       struct resource mem;
+       struct module *owner;
+       int irq;
        void __iomem *ipc_base;
        struct completion cmd_complete;
-       u8 irq_mode;
 };
 
-static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
-
 #define IPC_STATUS             0x04
 #define IPC_STATUS_IRQ         BIT(2)
 #define IPC_STATUS_ERR         BIT(1)
@@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */
 /* Timeout in jiffies */
 #define IPC_TIMEOUT            (3 * HZ)
 
+static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
 static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
 
+static struct class intel_scu_ipc_class = {
+       .name = "intel_scu_ipc",
+       .owner = THIS_MODULE,
+};
+
+/**
+ * intel_scu_ipc_dev_get() - Get SCU IPC instance
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Call intel_scu_ipc_dev_put() to release the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
+{
+       struct intel_scu_ipc_dev *scu = NULL;
+
+       mutex_lock(&ipclock);
+       if (ipcdev) {
+               get_device(&ipcdev->dev);
+               /*
+                * Prevent the IPC provider from being unloaded while it
+                * is being used.
+                */
+               if (!try_module_get(ipcdev->owner))
+                       put_device(&ipcdev->dev);
+               else
+                       scu = ipcdev;
+       }
+
+       mutex_unlock(&ipclock);
+       return scu;
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
+
+/**
+ * intel_scu_ipc_dev_put() - Put SCU IPC instance
+ * @scu: SCU IPC instance
+ *
+ * This function releases the SCU IPC instance retrieved from
+ * intel_scu_ipc_dev_get() and allows the driver providing IPC to be
+ * unloaded.
+ */
+void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
+{
+       if (scu) {
+               module_put(scu->owner);
+               put_device(&scu->dev);
+       }
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
+
+struct intel_scu_ipc_devres {
+       struct intel_scu_ipc_dev *scu;
+};
+
+static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
+{
+       struct intel_scu_ipc_devres *dr = res;
+       struct intel_scu_ipc_dev *scu = dr->scu;
+
+       intel_scu_ipc_dev_put(scu);
+}
+
+/**
+ * devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
+ * @dev: Device requesting the SCU IPC device
+ *
+ * The recommended new API takes SCU IPC instance as parameter and this
+ * function can be called by driver to get the instance. This also makes
+ * sure the driver providing the IPC functionality cannot be unloaded
+ * while the caller has the instance.
+ *
+ * Returns %NULL if SCU IPC is not currently available.
+ */
+struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
+{
+       struct intel_scu_ipc_devres *dr;
+       struct intel_scu_ipc_dev *scu;
+
+       dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return NULL;
+
+       scu = intel_scu_ipc_dev_get();
+       if (!scu) {
+               devres_free(dr);
+               return NULL;
+       }
+
+       dr->scu = scu;
+       devres_add(dev, dr);
+
+       return scu;
+}
+EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
+
 /*
  * Send ipc command
  * Command Register (Write Only):
@@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
                usleep_range(50, 100);
        } while (time_before(jiffies, end));
 
-       dev_err(scu->dev, "IPC timed out");
        return -ETIMEDOUT;
 }
 
@@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 {
        int status;
 
-       if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
-               dev_err(scu->dev, "IPC timed out\n");
+       if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
                return -ETIMEDOUT;
-       }
 
        status = ipc_read_status(scu);
        if (status & IPC_STATUS_ERR)
@@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
 
 static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
 {
-       return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
+       return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
 }
 
 /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
-static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
+static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                       u32 count, u32 op, u32 id)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
        int nc;
        u32 offset = 0;
        int err;
@@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
        memset(cbuf, 0, sizeof(cbuf));
 
        mutex_lock(&ipclock);
-
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
@@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
 }
 
 /**
- * intel_scu_ipc_ioread8               -       read a word via the SCU
+ * intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Return pointer for read byte
  *
@@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
  *
  * This function may sleep.
  */
-int intel_scu_ipc_ioread8(u16 addr, u8 *data)
+int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
 {
-       return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+       return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_ioread8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
 
 /**
- * intel_scu_ipc_iowrite8              -       write a byte via the SCU
+ * intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
+ * @scu: Optional SCU IPC instance
  * @addr: Register on SCU
  * @data: Byte to write
  *
@@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_iowrite8(u16 addr, u8 data)
+int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
 {
-       return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+       return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
+EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
 
 /**
- * intel_scu_ipc_readvv                -       read a set of registers
+ * intel_scu_ipc_dev_readv() - Read a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to return
  * @len: Length of array
@@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                           size_t len)
 {
-       return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
+       return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
 }
-EXPORT_SYMBOL(intel_scu_ipc_readv);
+EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
 
 /**
- * intel_scu_ipc_writev                -       write a set of registers
+ * intel_scu_ipc_dev_writev() - Write a set of registers
+ * @scu: Optional SCU IPC instance
  * @addr: Register list
  * @data: Bytes to write
  * @len: Length of array
@@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
  *
  * This function may sleep.
  */
-int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
+int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
+                            size_t len)
 {
-       return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
+       return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
 }
-EXPORT_SYMBOL(intel_scu_ipc_writev);
+EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
 
 /**
- * intel_scu_ipc_update_register       -       r/m/w a register
+ * intel_scu_ipc_dev_update() - Update a register
+ * @scu: Optional SCU IPC instance
  * @addr: Register address
- * @bits: Bits to update
+ * @data: Bits to update
  * @mask: Mask of bits to update
  *
  * Read-modify-write power control unit register. The first data argument
@@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
  * This function may sleep. Locking between SCU accesses is handled
  * for the caller.
  */
-int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
+int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
+                            u8 mask)
 {
-       u8 data[2] = { bits, mask };
-       return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
+       u8 tmp[2] = { data, mask };
+       return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
 }
-EXPORT_SYMBOL(intel_scu_ipc_update_register);
+EXPORT_SYMBOL(intel_scu_ipc_dev_update);
 
 /**
- * intel_scu_ipc_simple_command        -       send a simple command
+ * intel_scu_ipc_dev_simple_command() - Send a simple command
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  *
@@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
  * This function may sleep. Locking for SCU accesses is handled for the
  * caller.
  */
-int intel_scu_ipc_simple_command(int cmd, int sub)
+int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
+                                    int sub)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       u32 cmdval;
        int err;
 
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
-       ipc_command(scu, sub << 12 | cmd);
+       scu = ipcdev;
+       cmdval = sub << 12 | cmd;
+       ipc_command(scu, cmdval);
        err = intel_scu_ipc_check_status(scu);
        mutex_unlock(&ipclock);
+       if (err)
+               dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
        return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_simple_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
 
 /**
- * intel_scu_ipc_command       -       command with data
+ * intel_scu_ipc_command_with_size() - Command with data
+ * @scu: Optional SCU IPC instance
  * @cmd: Command
  * @sub: Sub type
  * @in: Input data
- * @inlen: Input length in dwords
+ * @inlen: Input length in bytes
+ * @size: Input size written to the IPC command register in whatever
+ *       units (dword, byte) the particular firmware requires. Normally
+ *       should be the same as @inlen.
  * @out: Output data
- * @outlen: Output length in dwords
+ * @outlen: Output length in bytes
  *
  * Issue a command to the SCU which involves data transfers. Do the
  * data copies under the lock but leave it for the caller to interpret.
  */
-int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
-                         u32 *out, int outlen)
+int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
+                                       int sub, const void *in, size_t inlen,
+                                       size_t size, void *out, size_t outlen)
 {
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
+       size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
+       u32 cmdval, inbuf[4] = {};
        int i, err;
 
+       if (inbuflen > 4 || outbuflen > 4)
+               return -EINVAL;
+
        mutex_lock(&ipclock);
-       if (scu->dev == NULL) {
+       if (!scu)
+               scu = ipcdev;
+       if (!scu) {
                mutex_unlock(&ipclock);
                return -ENODEV;
        }
 
-       for (i = 0; i < inlen; i++)
-               ipc_data_writel(scu, *in++, 4 * i);
+       memcpy(inbuf, in, inlen);
+       for (i = 0; i < inbuflen; i++)
+               ipc_data_writel(scu, inbuf[i], 4 * i);
 
-       ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
+       cmdval = (size << 16) | (sub << 12) | cmd;
+       ipc_command(scu, cmdval);
        err = intel_scu_ipc_check_status(scu);
 
        if (!err) {
-               for (i = 0; i < outlen; i++)
-                       *out++ = ipc_data_readl(scu, 4 * i);
+               u32 outbuf[4] = {};
+
+               for (i = 0; i < outbuflen; i++)
+                       outbuf[i] = ipc_data_readl(scu, 4 * i);
+
+               memcpy(out, outbuf, outlen);
        }
 
        mutex_unlock(&ipclock);
+       if (err)
+               dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
        return err;
 }
-EXPORT_SYMBOL(intel_scu_ipc_command);
+EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
 
 /*
  * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
@@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void intel_scu_ipc_release(struct device *dev)
+{
+       struct intel_scu_ipc_dev *scu;
+
+       scu = container_of(dev, struct intel_scu_ipc_dev, dev);
+       if (scu->irq > 0)
+               free_irq(scu->irq, scu);
+       iounmap(scu->ipc_base);
+       release_mem_region(scu->mem.start, resource_size(&scu->mem));
+       kfree(scu);
+}
+
 /**
- *     ipc_probe       -       probe an Intel SCU IPC
- *     @pdev: the PCI device matching
- *     @id: entry in the match table
+ * __intel_scu_ipc_register() - Register SCU IPC device
+ * @parent: Parent device
+ * @scu_data: Data used to configure SCU IPC
+ * @owner: Module registering the SCU IPC device
  *
- *     Enable and install an intel SCU IPC. This appears in the PCI space
- *     but uses some hard coded addresses as well.
+ * Call this function to register SCU IPC mechanism under @parent.
+ * Returns pointer to the new SCU IPC device or ERR_PTR() in case of
+ * failure. The caller may use the returned instance if it needs to do
+ * SCU IPC calls itself.
  */
-static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+struct intel_scu_ipc_dev *
+__intel_scu_ipc_register(struct device *parent,
+                        const struct intel_scu_ipc_data *scu_data,
+                        struct module *owner)
 {
        int err;
-       struct intel_scu_ipc_dev *scu = &ipcdev;
+       struct intel_scu_ipc_dev *scu;
+       void __iomem *ipc_base;
 
-       if (scu->dev)           /* We support only one SCU */
-               return -EBUSY;
+       mutex_lock(&ipclock);
+       /* We support only one IPC */
+       if (ipcdev) {
+               err = -EBUSY;
+               goto err_unlock;
+       }
 
-       err = pcim_enable_device(pdev);
-       if (err)
-               return err;
+       scu = kzalloc(sizeof(*scu), GFP_KERNEL);
+       if (!scu) {
+               err = -ENOMEM;
+               goto err_unlock;
+       }
 
-       err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
-       if (err)
-               return err;
+       scu->owner = owner;
+       scu->dev.parent = parent;
+       scu->dev.class = &intel_scu_ipc_class;
+       scu->dev.release = intel_scu_ipc_release;
+       dev_set_name(&scu->dev, "intel_scu_ipc");
+
+       if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem),
+                               "intel_scu_ipc")) {
+               err = -EBUSY;
+               goto err_free;
+       }
 
+       ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem));
+       if (!ipc_base) {
+               err = -ENOMEM;
+               goto err_release;
+       }
+
+       scu->ipc_base = ipc_base;
+       scu->mem = scu_data->mem;
+       scu->irq = scu_data->irq;
        init_completion(&scu->cmd_complete);
 
-       scu->ipc_base = pcim_iomap_table(pdev)[0];
+       if (scu->irq > 0) {
+               err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
+               if (err)
+                       goto err_unmap;
+       }
 
-       err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
-                              scu);
-       if (err)
-               return err;
+       /*
+        * After this point intel_scu_ipc_release() takes care of
+        * releasing the SCU IPC resources once refcount drops to zero.
+        */
+       err = device_register(&scu->dev);
+       if (err) {
+               put_device(&scu->dev);
+               goto err_unlock;
+       }
 
        /* Assign device at last */
-       scu->dev = &pdev->dev;
+       ipcdev = scu;
+       mutex_unlock(&ipclock);
 
-       intel_scu_devices_create();
+       return scu;
 
-       pci_set_drvdata(pdev, scu);
-       return 0;
+err_unmap:
+       iounmap(ipc_base);
+err_release:
+       release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem));
+err_free:
+       kfree(scu);
+err_unlock:
+       mutex_unlock(&ipclock);
+
+       return ERR_PTR(err);
 }
+EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
 
-static const struct pci_device_id pci_ids[] = {
-       { PCI_VDEVICE(INTEL, 0x080e) },
-       { PCI_VDEVICE(INTEL, 0x08ea) },
-       { PCI_VDEVICE(INTEL, 0x11a0) },
-       {}
-};
+/**
+ * intel_scu_ipc_unregister() - Unregister SCU IPC
+ * @scu: SCU IPC handle
+ *
+ * This unregisters the SCU IPC device and releases the acquired
+ * resources once the refcount goes to zero.
+ */
+void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
+{
+       mutex_lock(&ipclock);
+       if (!WARN_ON(!ipcdev)) {
+               ipcdev = NULL;
+               device_unregister(&scu->dev);
+       }
+       mutex_unlock(&ipclock);
+}
+EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
 
-static struct pci_driver ipc_driver = {
-       .driver = {
-               .suppress_bind_attrs = true,
-       },
-       .name = "intel_scu_ipc",
-       .id_table = pci_ids,
-       .probe = ipc_probe,
-};
-builtin_pci_driver(ipc_driver);
+static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
+{
+       struct intel_scu_ipc_devres *dr = res;
+       struct intel_scu_ipc_dev *scu = dr->scu;
+
+       intel_scu_ipc_unregister(scu);
+}
+
+/**
+ * __devm_intel_scu_ipc_register() - Register managed SCU IPC device
+ * @parent: Parent device
+ * @scu_data: Data used to configure SCU IPC
+ * @owner: Module registering the SCU IPC device
+ *
+ * Call this function to register managed SCU IPC mechanism under
+ * @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
+ * case of failure. The caller may use the returned instance if it needs
+ * to do SCU IPC calls itself.
+ */
+struct intel_scu_ipc_dev *
+__devm_intel_scu_ipc_register(struct device *parent,
+                             const struct intel_scu_ipc_data *scu_data,
+                             struct module *owner)
+{
+       struct intel_scu_ipc_devres *dr;
+       struct intel_scu_ipc_dev *scu;
+
+       dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
+       if (!dr)
+               return NULL;
+
+       scu = __intel_scu_ipc_register(parent, scu_data, owner);
+       if (IS_ERR(scu)) {
+               devres_free(dr);
+               return scu;
+       }
+
+       dr->scu = scu;
+       devres_add(parent, dr);
+
+       return scu;
+}
+EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
+
+static int __init intel_scu_ipc_init(void)
+{
+       return class_register(&intel_scu_ipc_class);
+}
+subsys_initcall(intel_scu_ipc_init);
+
+static void __exit intel_scu_ipc_exit(void)
+{
+       class_unregister(&intel_scu_ipc_class);
+}
+module_exit(intel_scu_ipc_exit);
index 8afe6fa06d7b8d45a1f9f68e5483f1e191f62e91..b7c10c15a3d633bf9b4c804f445935e2930c7d81 100644 (file)
@@ -22,6 +22,9 @@
 
 static int major;
 
+struct intel_scu_ipc_dev *scu;
+static DEFINE_MUTEX(scu_lock);
+
 /* IOCTL commands */
 #define        INTE_SCU_IPC_REGISTER_READ      0
 #define INTE_SCU_IPC_REGISTER_WRITE    1
@@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data  *data)
 
        switch (cmd) {
        case INTE_SCU_IPC_REGISTER_READ:
-               return intel_scu_ipc_readv(data->addr, data->data, count);
+               return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);
        case INTE_SCU_IPC_REGISTER_WRITE:
-               return intel_scu_ipc_writev(data->addr, data->data, count);
+               return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);
        case INTE_SCU_IPC_REGISTER_UPDATE:
-               return intel_scu_ipc_update_register(data->addr[0],
-                                                   data->data[0], data->mask);
+               return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
+                                               data->mask);
        default:
                return -ENOTTY;
        }
@@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
        return 0;
 }
 
+static int scu_ipc_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+
+       /* Only single open at the time */
+       mutex_lock(&scu_lock);
+       if (scu) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       scu = intel_scu_ipc_dev_get();
+       if (!scu)
+               ret = -ENODEV;
+
+unlock:
+       mutex_unlock(&scu_lock);
+       return ret;
+}
+
+static int scu_ipc_release(struct inode *inode, struct file *file)
+{
+       mutex_lock(&scu_lock);
+       intel_scu_ipc_dev_put(scu);
+       scu = NULL;
+       mutex_unlock(&scu_lock);
+
+       return 0;
+}
+
 static const struct file_operations scu_ipc_fops = {
        .unlocked_ioctl = scu_ipc_ioctl,
+       .open = scu_ipc_open,
+       .release = scu_ipc_release,
 };
 
 static int __init ipc_module_init(void)
diff --git a/drivers/platform/x86/intel_scu_pcidrv.c b/drivers/platform/x86/intel_scu_pcidrv.c
new file mode 100644 (file)
index 0000000..8c5fd82
--- /dev/null
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI driver for the Intel SCU.
+ *
+ * Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
+ * Authors: Sreedhara DS (sreedhara.ds@intel.com)
+ *         Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/intel-mid.h>
+#include <asm/intel_scu_ipc.h>
+
+static int intel_scu_pci_probe(struct pci_dev *pdev,
+                              const struct pci_device_id *id)
+{
+       void (*setup_fn)(void) = (void (*)(void))id->driver_data;
+       struct intel_scu_ipc_data scu_data = {};
+       struct intel_scu_ipc_dev *scu;
+       int ret;
+
+       ret = pcim_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       scu_data.mem = pdev->resource[0];
+       scu_data.irq = pdev->irq;
+
+       scu = intel_scu_ipc_register(&pdev->dev, &scu_data);
+       if (IS_ERR(scu))
+               return PTR_ERR(scu);
+
+       if (setup_fn)
+               setup_fn();
+       return 0;
+}
+
+static void intel_mid_scu_setup(void)
+{
+       intel_scu_devices_create();
+}
+
+static const struct pci_device_id pci_ids[] = {
+       { PCI_VDEVICE(INTEL, 0x080e),
+         .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+       { PCI_VDEVICE(INTEL, 0x08ea),
+         .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+       { PCI_VDEVICE(INTEL, 0x0a94) },
+       { PCI_VDEVICE(INTEL, 0x11a0),
+         .driver_data = (kernel_ulong_t)intel_mid_scu_setup },
+       { PCI_VDEVICE(INTEL, 0x1a94) },
+       { PCI_VDEVICE(INTEL, 0x5a94) },
+       {}
+};
+
+static struct pci_driver intel_scu_pci_driver = {
+       .driver = {
+               .suppress_bind_attrs = true,
+       },
+       .name = "intel_scu",
+       .id_table = pci_ids,
+       .probe = intel_scu_pci_probe,
+};
+
+builtin_pci_driver(intel_scu_pci_driver);
diff --git a/drivers/platform/x86/intel_scu_pltdrv.c b/drivers/platform/x86/intel_scu_pltdrv.c
new file mode 100644 (file)
index 0000000..56ec6ae
--- /dev/null
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Platform driver for the Intel SCU.
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Authors: Divya Sasidharan <divya.s.sasidharan@intel.com>
+ *         Mika Westerberg <mika.westerberg@linux.intel.com>
+ *         Rajmohan Mani <rajmohan.mani@intel.com>
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/intel_scu_ipc.h>
+
+static int intel_scu_platform_probe(struct platform_device *pdev)
+{
+       struct intel_scu_ipc_data scu_data = {};
+       struct intel_scu_ipc_dev *scu;
+       const struct resource *res;
+
+       scu_data.irq = platform_get_irq_optional(pdev, 0);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOMEM;
+
+       scu_data.mem = *res;
+
+       scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
+       if (IS_ERR(scu))
+               return PTR_ERR(scu);
+
+       platform_set_drvdata(pdev, scu);
+       return 0;
+}
+
+static const struct acpi_device_id intel_scu_acpi_ids[] = {
+       { "INTC1026" },
+       {}
+};
+MODULE_DEVICE_TABLE(acpi, intel_scu_acpi_ids);
+
+static struct platform_driver intel_scu_platform_driver = {
+       .probe = intel_scu_platform_probe,
+       .driver = {
+               .name = "intel_scu",
+               .acpi_match_table = intel_scu_acpi_ids,
+       },
+};
+module_platform_driver(intel_scu_platform_driver);
+
+MODULE_AUTHOR("Divya Sasidharan <divya.s.sasidharan@intel.com>");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com");
+MODULE_AUTHOR("Rajmohan Mani <rajmohan.mani@intel.com>");
+MODULE_DESCRIPTION("Intel SCU platform driver");
+MODULE_LICENSE("GPL v2");
index de4169d0796bd846ef2416725308cd3bbfea92c8..d84e2174cbdebe71d9bd48b547ac4cf9c846e3de 100644 (file)
 #define PUNIT_MAILBOX_BUSY_BIT         31
 
 /*
- * Commands has variable amount of processing time. Most of the commands will
- * be done in 0-3 tries, but some takes up to 50.
- * The real processing time was observed as 25us for the most of the commands
- * at 2GHz. It is possible to optimize this count taking samples on customer
- * systems.
+ * The average time to complete some commands is about 40us. The current
+ * count is enough to satisfy 40us. But when the firmware is very busy, this
+ * causes timeout occasionally.  So increase to deal with some worst case
+ * scenarios. Most of the command still complete in few us.
  */
-#define OS_MAILBOX_RETRY_COUNT         50
+#define OS_MAILBOX_RETRY_COUNT         100
 
 struct isst_if_device {
        struct mutex mutex;
index d4040bb222b485ead8c8caa8a1b3c4463e72dffa..fdf55b5d69480e4a12765a15b42bb8ac6dee287b 100644 (file)
@@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
 EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
 
 /**
- * telemetry_pltconfig_valid() - Checkif platform config is valid
+ * telemetry_get_pltdata() - Return telemetry platform config
  *
- * Usage by other than telemetry module is invalid
- *
- * Return: 0 success, < 0 for failure
+ * May be used by other telemetry modules to get platform specific
+ * configuration.
  */
-int telemetry_pltconfig_valid(void)
+struct telemetry_plt_config *telemetry_get_pltdata(void)
 {
-       if (telm_core_conf.plt_config)
-               return 0;
-
-       else
-               return -EINVAL;
+       return telm_core_conf.plt_config;
 }
-EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
+EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
 
 static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
                                           const char **name, int len)
index 8a53d3b485b3a04cfc10cc9f274bb1ab2c892a99..1d4d0fbfd63cc6a07f1094b4203692cfe253781c 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/mfd/intel_pmc_bxt.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/seq_file.h>
@@ -22,7 +23,6 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/intel_pmc_ipc.h>
 #include <asm/intel_telemetry.h>
 
 #define DRIVER_NAME                    "telemetry_soc_debugfs"
@@ -647,10 +647,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states);
 
 static int telem_s0ix_res_get(void *data, u64 *val)
 {
+       struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
        u64 s0ix_total_res;
        int ret;
 
-       ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
+       ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res);
        if (ret) {
                pr_err("Failed to read S0ix residency");
                return ret;
@@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void)
         */
        if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
            suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
-               ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
+               struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
+               struct intel_pmc_dev *pmc = plt_config->pmc;
+
+               ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG,
                                          &suspend_shlw_res_exit);
                if (ret < 0)
                        goto out;
 
-               ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
+               ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG,
                                          &suspend_deep_res_exit);
                if (ret < 0)
                        goto out;
@@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void)
 
        debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
 
-       err = telemetry_pltconfig_valid();
-       if (err < 0) {
+       if (!telemetry_get_pltdata()) {
                pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
                return -ENODEV;
        }
index 987a24e3344e920413437436e7e0a26080ce3053..405dea87de6bf8f4c53a392cbb09fc420d12c051 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
-#include <asm/intel_pmc_ipc.h>
 #include <asm/intel_punit_ipc.h>
 #include <asm/intel_telemetry.h>
 
@@ -35,6 +34,7 @@
 #define TELEM_SSRAM_STARTTIME_OFFSET   8
 #define TELEM_SSRAM_EVTLOG_OFFSET      16
 
+#define IOSS_TELEM                     0xeb
 #define IOSS_TELEM_EVENT_READ          0x0
 #define IOSS_TELEM_EVENT_WRITE         0x1
 #define IOSS_TELEM_INFO_READ           0x2
@@ -42,9 +42,6 @@
 #define IOSS_TELEM_TRACE_CTL_WRITE     0x6
 #define IOSS_TELEM_EVENT_CTL_READ      0x7
 #define IOSS_TELEM_EVENT_CTL_WRITE     0x8
-#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4
-#define IOSS_TELEM_READ_WORD           0x1
-#define IOSS_TELEM_WRITE_FOURBYTES     0x4
 #define IOSS_TELEM_EVT_WRITE_SIZE      0x3
 
 #define TELEM_INFO_SRAMEVTS_MASK       0xFF00
@@ -250,17 +247,14 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
 static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
 {
        u32 write_buf;
-       int ret;
 
        write_buf = evt_id | TELEM_EVENT_ENABLE;
        write_buf <<= BITS_PER_BYTE;
        write_buf |= index;
 
-       ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                                   IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
-                                   IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
-
-       return ret;
+       return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+                                        IOSS_TELEM_EVENT_WRITE, &write_buf,
+                                        IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
 }
 
 static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
@@ -278,6 +272,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
 static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
                                         enum telemetry_action action)
 {
+       struct intel_scu_ipc_dev *scu = telm_conf->scu;
        u8 num_ioss_evts, ioss_period;
        int ret, index, idx;
        u32 *ioss_evtmap;
@@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
        ioss_evtmap = evtconfig.evtmap;
 
        /* Get telemetry EVENT CTL */
-       ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+       ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
                                    IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-                                   &telem_ctrl, IOSS_TELEM_READ_WORD);
+                                   &telem_ctrl, sizeof(telem_ctrl));
        if (ret) {
                pr_err("IOSS TELEM_CTRL Read Failed\n");
                return ret;
@@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
        /* Disable Telemetry */
        TELEM_DISABLE(telem_ctrl);
 
-       ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                                   IOSS_TELEM_EVENT_CTL_WRITE,
-                                   (u8 *)&telem_ctrl,
-                                   IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-                                   NULL, 0);
+       ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+                                   IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
+                                   sizeof(telem_ctrl), NULL, 0);
        if (ret) {
                pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
                return ret;
@@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
                /* Clear All Events */
                TELEM_CLEAR_EVENTS(telem_ctrl);
 
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+               ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
                                            IOSS_TELEM_EVENT_CTL_WRITE,
-                                           (u8 *)&telem_ctrl,
-                                           IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
+                                           &telem_ctrl, sizeof(telem_ctrl),
                                            NULL, 0);
                if (ret) {
                        pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -344,10 +336,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
                /* Clear All Events */
                TELEM_CLEAR_EVENTS(telem_ctrl);
 
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+               ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
                                            IOSS_TELEM_EVENT_CTL_WRITE,
-                                           (u8 *)&telem_ctrl,
-                                           IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
+                                           &telem_ctrl, sizeof(telem_ctrl),
                                            NULL, 0);
                if (ret) {
                        pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
@@ -396,10 +387,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
        TELEM_ENABLE_PERIODIC(telem_ctrl);
        telem_ctrl |= ioss_period;
 
-       ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+       ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
                                    IOSS_TELEM_EVENT_CTL_WRITE,
-                                   (u8 *)&telem_ctrl,
-                                   IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
+                                   &telem_ctrl, sizeof(telem_ctrl), NULL, 0);
        if (ret) {
                pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
                return ret;
@@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev)
        u32 read_buf, events, event_regs;
        int ret;
 
-       ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
-                                   NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
+       ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+                                       IOSS_TELEM_INFO_READ, NULL, 0,
+                                       &read_buf, sizeof(read_buf));
        if (ret) {
                dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
                return ret;
@@ -681,6 +672,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
 
        mutex_lock(&(telm_conf->telem_lock));
        if (ioss_period) {
+               struct intel_scu_ipc_dev *scu = telm_conf->scu;
+
                if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
                        pr_err("IOSS Sampling Period Out of Range\n");
                        ret = -EINVAL;
@@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
                }
 
                /* Get telemetry EVENT CTL */
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
+               ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
                                            IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
-                                           &telem_ctrl, IOSS_TELEM_READ_WORD);
+                                           &telem_ctrl, sizeof(telem_ctrl));
                if (ret) {
                        pr_err("IOSS TELEM_CTRL Read Failed\n");
                        goto out;
@@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
                /* Disable Telemetry */
                TELEM_DISABLE(telem_ctrl);
 
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                                           IOSS_TELEM_EVENT_CTL_WRITE,
-                                           (u8 *)&telem_ctrl,
-                                           IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-                                           NULL, 0);
+               ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+                                               IOSS_TELEM_EVENT_CTL_WRITE,
+                                               &telem_ctrl, sizeof(telem_ctrl),
+                                               NULL, 0);
                if (ret) {
                        pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
                        goto out;
@@ -715,11 +707,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
                TELEM_ENABLE_PERIODIC(telem_ctrl);
                telem_ctrl |= ioss_period;
 
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                                           IOSS_TELEM_EVENT_CTL_WRITE,
-                                           (u8 *)&telem_ctrl,
-                                           IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
-                                           NULL, 0);
+               ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
+                                               IOSS_TELEM_EVENT_CTL_WRITE,
+                                               &telem_ctrl, sizeof(telem_ctrl),
+                                               NULL, 0);
                if (ret) {
                        pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
                        goto out;
@@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
                break;
 
        case TELEM_IOSS:
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                               IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-                               IOSS_TELEM_READ_WORD);
+               ret = intel_scu_ipc_dev_command(telm_conf->scu,
+                               IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
+                               NULL, 0, &temp, sizeof(temp));
                if (ret) {
                        pr_err("IOSS TRACE_CTL Read Failed\n");
                        goto out;
@@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
                break;
 
        case TELEM_IOSS:
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                               IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
-                               IOSS_TELEM_READ_WORD);
+               ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+                                               IOSS_TELEM_TRACE_CTL_READ,
+                                               NULL, 0, &temp, sizeof(temp));
                if (ret) {
                        pr_err("IOSS TRACE_CTL Read Failed\n");
                        goto out;
@@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
                TELEM_CLEAR_VERBOSITY_BITS(temp);
                TELEM_SET_VERBOSITY_BITS(temp, verbosity);
 
-               ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
-                               IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
-                               IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
+               ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
+                                               IOSS_TELEM_TRACE_CTL_WRITE,
+                                               &temp, sizeof(temp), NULL, 0);
                if (ret) {
                        pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
                        goto out;
@@ -1124,6 +1115,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 
        telm_conf = (struct telemetry_plt_config *)id->driver_data;
 
+       telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
+
        mem = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(mem))
                return PTR_ERR(mem);
@@ -1136,6 +1129,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 
        telm_conf->ioss_config.regmap = mem;
 
+       telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
+       if (!telm_conf->scu) {
+               ret = -EPROBE_DEFER;
+               goto out;
+       }
+
        mutex_init(&telm_conf->telem_lock);
        mutex_init(&telm_conf->telem_trace_lock);
 
index c0bb1f864dfeb51dd9db5f5c57cfdc0109d946b2..dd900a76d8de5b3846407c88eb565a5eb2b2a3e3 100644 (file)
@@ -67,9 +67,7 @@ static u32 inited;
 #define INIT_INPUT_WMI_0        0x01
 #define INIT_INPUT_WMI_2        0x02
 #define INIT_INPUT_ACPI         0x04
-#define INIT_TPAD_LED           0x08
-#define INIT_KBD_LED            0x10
-#define INIT_SPARSE_KEYMAP        0x80
+#define INIT_SPARSE_KEYMAP      0x80
 
 static const struct key_entry wmi_keymap[] = {
        {KE_KEY, 0x70, {KEY_F15} },      /* LG control panel (F1) */
@@ -626,11 +624,9 @@ static int acpi_add(struct acpi_device *device)
        if (ret)
                goto out_platform_device;
 
-       if (!led_classdev_register(&pf_device->dev, &kbd_backlight))
-               inited |= INIT_KBD_LED;
-
-       if (!led_classdev_register(&pf_device->dev, &tpad_led))
-               inited |= INIT_TPAD_LED;
+       /* LEDs are optional */
+       led_classdev_register(&pf_device->dev, &kbd_backlight);
+       led_classdev_register(&pf_device->dev, &tpad_led);
 
        wmi_input_setup();
 
@@ -646,11 +642,9 @@ out_platform_registered:
 static int acpi_remove(struct acpi_device *device)
 {
        sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
-       if (inited & INIT_KBD_LED)
-               led_classdev_unregister(&kbd_backlight);
 
-       if (inited & INIT_TPAD_LED)
-               led_classdev_unregister(&tpad_led);
+       led_classdev_unregister(&tpad_led);
+       led_classdev_unregister(&kbd_backlight);
 
        wmi_input_destroy();
        platform_device_unregister(pf_device);
index 23e40aa2176e861ce98db35e7ec4ca6ae1d5c3e9..d5cec6e35bb83618e74168bfbffd31de2f5bf2a8 100644 (file)
@@ -1138,8 +1138,7 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
 
 static void samsung_leds_exit(struct samsung_laptop *samsung)
 {
-       if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
-               led_classdev_unregister(&samsung->kbd_led);
+       led_classdev_unregister(&samsung->kbd_led);
        if (samsung->led_workqueue)
                destroy_workqueue(samsung->led_workqueue);
 }
index 51309f7ceedecdcbe24df5689e1c05b1ee8c1450..e5a1b553340813dd140459084b4d80089dfd301e 100644 (file)
@@ -757,33 +757,6 @@ static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
        return result;
 }
 
-static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
-               int *result)
-{
-       union acpi_object *object = NULL;
-       if (value) {
-               u64 v = *value;
-               object = __call_snc_method(handle, name, &v);
-       } else
-               object = __call_snc_method(handle, name, NULL);
-
-       if (!object)
-               return -EINVAL;
-
-       if (object->type != ACPI_TYPE_INTEGER) {
-               pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
-                               ACPI_TYPE_INTEGER, object->type);
-               kfree(object);
-               return -EINVAL;
-       }
-
-       if (result)
-               *result = object->integer.value;
-
-       kfree(object);
-       return 0;
-}
-
 #define MIN(a, b)      (a > b ? b : a)
 static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
                void *buffer, size_t buflen)
@@ -795,17 +768,20 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
        if (!object)
                return -EINVAL;
 
-       if (object->type == ACPI_TYPE_BUFFER) {
+       if (!buffer) {
+               /* do nothing */
+       } else if (object->type == ACPI_TYPE_BUFFER) {
                len = MIN(buflen, object->buffer.length);
+               memset(buffer, 0, buflen);
                memcpy(buffer, object->buffer.pointer, len);
 
        } else if (object->type == ACPI_TYPE_INTEGER) {
                len = MIN(buflen, sizeof(object->integer.value));
+               memset(buffer, 0, buflen);
                memcpy(buffer, &object->integer.value, len);
 
        } else {
-               pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
-                               ACPI_TYPE_BUFFER, object->type);
+               pr_warn("Unexpected acpi_object: 0x%x\n", object->type);
                ret = -EINVAL;
        }
 
@@ -813,6 +789,23 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
        return ret;
 }
 
+static int sony_nc_int_call(acpi_handle handle, char *name, int *value, int
+               *result)
+{
+       int ret;
+
+       if (value) {
+               u64 v = *value;
+
+               ret = sony_nc_buffer_call(handle, name, &v, result,
+                               sizeof(*result));
+       } else {
+               ret =  sony_nc_buffer_call(handle, name, NULL, result,
+                               sizeof(*result));
+       }
+       return ret;
+}
+
 struct sony_nc_handles {
        u16 cap[0x10];
        struct device_attribute devattr;
@@ -2295,7 +2288,12 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
 #ifdef CONFIG_PM_SLEEP
 static void sony_nc_thermal_resume(void)
 {
-       unsigned int status = sony_nc_thermal_mode_get();
+       int status;
+
+       if (!th_handle)
+               return;
+
+       status = sony_nc_thermal_mode_get();
 
        if (status != th_handle->mode)
                sony_nc_thermal_mode_set(th_handle->mode);
index 0f704484ae1d6b7a4d6fc7abb08fb3f268d1dce1..ff7f0a4f247563f4ebf3c9cce86da52eee1e2a37 100644 (file)
@@ -318,6 +318,7 @@ static struct {
        u32 uwb:1;
        u32 fan_ctrl_status_undef:1;
        u32 second_fan:1;
+       u32 second_fan_ctl:1;
        u32 beep_needs_two_args:1;
        u32 mixer_no_level_control:1;
        u32 battery_force_primary:1;
@@ -884,20 +885,11 @@ static ssize_t dispatch_proc_write(struct file *file,
 
        if (!ibm || !ibm->write)
                return -EINVAL;
-       if (count > PAGE_SIZE - 2)
-               return -EINVAL;
-
-       kernbuf = kmalloc(count + 2, GFP_KERNEL);
-       if (!kernbuf)
-               return -ENOMEM;
 
-       if (copy_from_user(kernbuf, userbuf, count)) {
-               kfree(kernbuf);
-               return -EFAULT;
-       }
+       kernbuf = strndup_user(userbuf, PAGE_SIZE);
+       if (IS_ERR(kernbuf))
+               return PTR_ERR(kernbuf);
 
-       kernbuf[count] = 0;
-       strcat(kernbuf, ",");
        ret = ibm->write(kernbuf);
        if (ret == 0)
                ret = count;
@@ -915,23 +907,6 @@ static const struct proc_ops dispatch_proc_ops = {
        .proc_write     = dispatch_proc_write,
 };
 
-static char *next_cmd(char **cmds)
-{
-       char *start = *cmds;
-       char *end;
-
-       while ((end = strchr(start, ',')) && end == start)
-               start = end + 1;
-
-       if (!end)
-               return NULL;
-
-       *end = 0;
-       *cmds = end + 1;
-       return start;
-}
-
-
 /****************************************************************************
  ****************************************************************************
  *
@@ -1422,7 +1397,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
        if (id >= TPACPI_RFK_SW_MAX)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "enable") == 0)
                        status = TPACPI_RFK_RADIO_ON;
                else if (strlencmp(cmd, "disable") == 0)
@@ -4305,7 +4280,7 @@ static int hotkey_write(char *buf)
        mask = hotkey_user_mask;
 
        res = 0;
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "enable") == 0) {
                        hotkey_enabledisable_warn(1);
                } else if (strlencmp(cmd, "disable") == 0) {
@@ -5232,7 +5207,7 @@ static int video_write(char *buf)
        enable = 0;
        disable = 0;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "lcd_enable") == 0) {
                        enable |= TP_ACPI_VIDEO_S_LCD;
                } else if (strlencmp(cmd, "lcd_disable") == 0) {
@@ -5433,8 +5408,7 @@ static int __init kbdlight_init(struct ibm_init_struct *iibm)
 
 static void kbdlight_exit(void)
 {
-       if (tp_features.kbdlight)
-               led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
+       led_classdev_unregister(&tpacpi_led_kbdlight.led_classdev);
 }
 
 static int kbdlight_set_level_and_update(int level)
@@ -5472,23 +5446,18 @@ static int kbdlight_read(struct seq_file *m)
 static int kbdlight_write(char *buf)
 {
        char *cmd;
-       int level = -1;
+       int res, level = -EINVAL;
 
        if (!tp_features.kbdlight)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "0") == 0)
-                       level = 0;
-               else if (strlencmp(cmd, "1") == 0)
-                       level = 1;
-               else if (strlencmp(cmd, "2") == 0)
-                       level = 2;
-               else
-                       return -EINVAL;
+       while ((cmd = strsep(&buf, ","))) {
+               res = kstrtoint(cmd, 10, &level);
+               if (res < 0)
+                       return res;
        }
 
-       if (level == -1)
+       if (level >= 3 || level < 0)
                return -EINVAL;
 
        return kbdlight_set_level_and_update(level);
@@ -5657,7 +5626,7 @@ static int light_write(char *buf)
        if (!tp_features.light)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "on") == 0) {
                        newstatus = 1;
                } else if (strlencmp(cmd, "off") == 0) {
@@ -5742,7 +5711,7 @@ static int cmos_write(char *buf)
        char *cmd;
        int cmos_cmd, res;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
                    cmos_cmd >= 0 && cmos_cmd <= 21) {
                        /* cmos_cmd set */
@@ -5948,20 +5917,14 @@ static void led_exit(void)
 {
        unsigned int i;
 
-       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
-               if (tpacpi_leds[i].led_classdev.name)
-                       led_classdev_unregister(&tpacpi_leds[i].led_classdev);
-       }
+       for (i = 0; i < TPACPI_LED_NUMLEDS; i++)
+               led_classdev_unregister(&tpacpi_leds[i].led_classdev);
 
        kfree(tpacpi_leds);
 }
 
 static int __init tpacpi_init_led(unsigned int led)
 {
-       int rc;
-
-       tpacpi_leds[led].led = led;
-
        /* LEDs with no name don't get registered */
        if (!tpacpi_led_names[led])
                return 0;
@@ -5969,17 +5932,12 @@ static int __init tpacpi_init_led(unsigned int led)
        tpacpi_leds[led].led_classdev.brightness_set_blocking = &led_sysfs_set;
        tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
        if (led_supported == TPACPI_LED_570)
-               tpacpi_leds[led].led_classdev.brightness_get =
-                                               &led_sysfs_get;
+               tpacpi_leds[led].led_classdev.brightness_get = &led_sysfs_get;
 
        tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
+       tpacpi_leds[led].led = led;
 
-       rc = led_classdev_register(&tpacpi_pdev->dev,
-                               &tpacpi_leds[led].led_classdev);
-       if (rc < 0)
-               tpacpi_leds[led].led_classdev.name = NULL;
-
-       return rc;
+       return led_classdev_register(&tpacpi_pdev->dev, &tpacpi_leds[led].led_classdev);
 }
 
 static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
@@ -6089,8 +6047,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
        for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
                tpacpi_leds[i].led = -1;
 
-               if (!tpacpi_is_led_restricted(i) &&
-                   test_bit(i, &useful_leds)) {
+               if (!tpacpi_is_led_restricted(i) && test_bit(i, &useful_leds)) {
                        rc = tpacpi_init_led(i);
                        if (rc < 0) {
                                led_exit();
@@ -6143,12 +6100,14 @@ static int led_write(char *buf)
        if (!led_supported)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%d", &led) != 1)
                        return -EINVAL;
 
-               if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
-                               tpacpi_leds[led].led < 0)
+               if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1))
+                       return -ENODEV;
+
+               if (tpacpi_leds[led].led < 0)
                        return -ENODEV;
 
                if (strstr(cmd, "off")) {
@@ -6228,7 +6187,7 @@ static int beep_write(char *buf)
        if (!beep_handle)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
                    beep_cmd >= 0 && beep_cmd <= 17) {
                        /* beep_cmd set */
@@ -7116,7 +7075,7 @@ static int brightness_write(char *buf)
        if (level < 0)
                return level;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (strlencmp(cmd, "up") == 0) {
                        if (level < bright_maxlvl)
                                level++;
@@ -7868,7 +7827,7 @@ static int volume_write(char *buf)
        new_level = s & TP_EC_AUDIO_LVL_MSK;
        new_mute  = s & TP_EC_AUDIO_MUTESW_MSK;
 
-       while ((cmd = next_cmd(&buf))) {
+       while ((cmd = strsep(&buf, ","))) {
                if (!tp_features.mixer_no_level_control) {
                        if (strlencmp(cmd, "up") == 0) {
                                if (new_mute)
@@ -8324,11 +8283,19 @@ static int fan_set_level(int level)
 
        switch (fan_control_access_mode) {
        case TPACPI_FAN_WR_ACPI_SFAN:
-               if (level >= 0 && level <= 7) {
-                       if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
-                               return -EIO;
-               } else
+               if ((level < 0) || (level > 7))
                        return -EINVAL;
+
+               if (tp_features.second_fan_ctl) {
+                       if (!fan_select_fan2() ||
+                           !acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) {
+                               pr_warn("Couldn't set 2nd fan level, disabling support\n");
+                               tp_features.second_fan_ctl = 0;
+                       }
+                       fan_select_fan1();
+               }
+               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+                       return -EIO;
                break;
 
        case TPACPI_FAN_WR_ACPI_FANS:
@@ -8345,6 +8312,15 @@ static int fan_set_level(int level)
                else if (level & TP_EC_FAN_AUTO)
                        level |= 4;     /* safety min speed 4 */
 
+               if (tp_features.second_fan_ctl) {
+                       if (!fan_select_fan2() ||
+                           !acpi_ec_write(fan_status_offset, level)) {
+                               pr_warn("Couldn't set 2nd fan level, disabling support\n");
+                               tp_features.second_fan_ctl = 0;
+                       }
+                       fan_select_fan1();
+
+               }
                if (!acpi_ec_write(fan_status_offset, level))
                        return -EIO;
                else
@@ -8763,6 +8739,7 @@ static const struct attribute_group fan_attr_group = {
 
 #define TPACPI_FAN_Q1  0x0001          /* Unitialized HFSP */
 #define TPACPI_FAN_2FAN        0x0002          /* EC 0x31 bit 0 selects fan2 */
+#define TPACPI_FAN_2CTL        0x0004          /* selects fan2 control */
 
 static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_QEC_IBM('1', 'Y', TPACPI_FAN_Q1),
@@ -8771,6 +8748,13 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_QEC_IBM('7', '0', TPACPI_FAN_Q1),
        TPACPI_QEC_LNV('7', 'M', TPACPI_FAN_2FAN),
        TPACPI_Q_LNV('N', '1', TPACPI_FAN_2FAN),
+       TPACPI_Q_LNV3('N', '1', 'D', TPACPI_FAN_2CTL),  /* P70 */
+       TPACPI_Q_LNV3('N', '1', 'E', TPACPI_FAN_2CTL),  /* P50 */
+       TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL),  /* P71 */
+       TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL),  /* P51 */
+       TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL),  /* P52 / P72 */
+       TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (1st gen) */
+       TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (2nd gen) */
 };
 
 static int __init fan_init(struct ibm_init_struct *iibm)
@@ -8788,6 +8772,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
        fan_watchdog_maxinterval = 0;
        tp_features.fan_ctrl_status_undef = 0;
        tp_features.second_fan = 0;
+       tp_features.second_fan_ctl = 0;
        fan_control_desired_level = 7;
 
        if (tpacpi_is_ibm()) {
@@ -8812,8 +8797,12 @@ static int __init fan_init(struct ibm_init_struct *iibm)
                                fan_quirk1_setup();
                        if (quirks & TPACPI_FAN_2FAN) {
                                tp_features.second_fan = 1;
-                               dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
-                                       "secondary fan support enabled\n");
+                               pr_info("secondary fan support enabled\n");
+                       }
+                       if (quirks & TPACPI_FAN_2CTL) {
+                               tp_features.second_fan = 1;
+                               tp_features.second_fan_ctl = 1;
+                               pr_info("secondary fan control enabled\n");
                        }
                } else {
                        pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
@@ -9148,7 +9137,7 @@ static int fan_write(char *buf)
        char *cmd;
        int rc = 0;
 
-       while (!rc && (cmd = next_cmd(&buf))) {
+       while (!rc && (cmd = strsep(&buf, ","))) {
                if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
                      fan_write_cmd_level(cmd, &rc)) &&
                    !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
@@ -9271,10 +9260,8 @@ static int mute_led_init(struct ibm_init_struct *iibm)
                mute_led_cdev[i].brightness = ledtrig_audio_get(i);
                err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]);
                if (err < 0) {
-                       while (i--) {
-                               if (led_tables[i].state >= 0)
-                                       led_classdev_unregister(&mute_led_cdev[i]);
-                       }
+                       while (i--)
+                               led_classdev_unregister(&mute_led_cdev[i]);
                        return err;
                }
        }
@@ -9286,10 +9273,8 @@ static void mute_led_exit(void)
        int i;
 
        for (i = 0; i < TPACPI_LED_MAX; i++) {
-               if (led_tables[i].state >= 0) {
-                       led_classdev_unregister(&mute_led_cdev[i]);
-                       tpacpi_led_set(i, false);
-               }
+               led_classdev_unregister(&mute_led_cdev[i]);
+               tpacpi_led_set(i, false);
        }
 }
 
@@ -9786,19 +9771,18 @@ static int lcdshadow_read(struct seq_file *m)
 static int lcdshadow_write(char *buf)
 {
        char *cmd;
-       int state = -1;
+       int res, state = -EINVAL;
 
        if (lcdshadow_state < 0)
                return -ENODEV;
 
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "0") == 0)
-                       state = 0;
-               else if (strlencmp(cmd, "1") == 0)
-                       state = 1;
+       while ((cmd = strsep(&buf, ","))) {
+               res = kstrtoint(cmd, 10, &state);
+               if (res < 0)
+                       return res;
        }
 
-       if (state == -1)
+       if (state >= 2 || state < 0)
                return -EINVAL;
 
        return lcdshadow_set(state);
@@ -10314,10 +10298,9 @@ static int __init set_ibm_param(const char *val, const struct kernel_param *kp)
                        continue;
 
                if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
-                       if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+                       if (strlen(val) > sizeof(ibms_init[i].param) - 1)
                                return -ENOSPC;
                        strcpy(ibms_init[i].param, val);
-                       strcat(ibms_init[i].param, ",");
                        return 0;
                }
        }
index 80894454673947696747d770627a96dd37206609..1ddab5a6dead6ddf0bc6e821ba0988b51758e4c6 100644 (file)
@@ -205,9 +205,6 @@ struct toshiba_acpi_dev {
        unsigned int special_functions;
 
        bool kbd_event_generated;
-       bool kbd_led_registered;
-       bool illumination_led_registered;
-       bool eco_led_registered;
        bool killswitch;
 };
 
@@ -458,7 +455,6 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
        acpi_status status;
 
        dev->illumination_supported = 0;
-       dev->illumination_led_registered = false;
 
        if (!sci_open(dev))
                return;
@@ -528,7 +524,6 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
        acpi_status status;
 
        dev->kbd_illum_supported = 0;
-       dev->kbd_led_registered = false;
        dev->kbd_event_generated = false;
 
        if (!sci_open(dev))
@@ -673,7 +668,6 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
        acpi_status status;
 
        dev->eco_supported = 0;
-       dev->eco_led_registered = false;
 
        status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
@@ -2993,14 +2987,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
 
        backlight_device_unregister(dev->backlight_dev);
 
-       if (dev->illumination_led_registered)
-               led_classdev_unregister(&dev->led_dev);
-
-       if (dev->kbd_led_registered)
-               led_classdev_unregister(&dev->kbd_led);
-
-       if (dev->eco_led_registered)
-               led_classdev_unregister(&dev->eco_led);
+       led_classdev_unregister(&dev->led_dev);
+       led_classdev_unregister(&dev->kbd_led);
+       led_classdev_unregister(&dev->eco_led);
 
        if (dev->wwan_rfk) {
                rfkill_unregister(dev->wwan_rfk);
@@ -3092,8 +3081,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                dev->led_dev.max_brightness = 1;
                dev->led_dev.brightness_set = toshiba_illumination_set;
                dev->led_dev.brightness_get = toshiba_illumination_get;
-               if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
-                       dev->illumination_led_registered = true;
+               led_classdev_register(&acpi_dev->dev, &dev->led_dev);
        }
 
        toshiba_eco_mode_available(dev);
@@ -3102,8 +3090,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                dev->eco_led.max_brightness = 1;
                dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
                dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
-               if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
-                       dev->eco_led_registered = true;
+               led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led);
        }
 
        toshiba_kbd_illum_available(dev);
@@ -3119,8 +3106,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
                dev->kbd_led.max_brightness = 1;
                dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
                dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
-               if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
-                       dev->kbd_led_registered = true;
+               led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led);
        }
 
        ret = toshiba_touchpad_get(dev, &dummy);
index 6ec8923dec1a5f6d698d95b771e01312346c7817..5c223015ee71b2db7b2737137ded8678a024e04e 100644 (file)
@@ -373,6 +373,23 @@ static const struct ts_dmi_data jumper_ezpad_mini3_data = {
        .properties     = jumper_ezpad_mini3_props,
 };
 
+static const struct property_entry mpman_mpwin895cl_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-min-x", 3),
+       PROPERTY_ENTRY_U32("touchscreen-min-y", 9),
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-mpman-mpwin895cl.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct ts_dmi_data mpman_mpwin895cl_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = mpman_mpwin895cl_props,
+};
+
 static const struct property_entry myria_my8307_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-x", 1720),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
@@ -448,6 +465,24 @@ static const struct ts_dmi_data onda_v820w_32g_data = {
        .properties     = onda_v820w_32g_props,
 };
 
+static const struct property_entry onda_v891_v5_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1715),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1140),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name",
+                             "gsl3676-onda-v891-v5.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct ts_dmi_data onda_v891_v5_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = onda_v891_v5_props,
+};
+
 static const struct property_entry onda_v891w_v1_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-min-x", 46),
        PROPERTY_ENTRY_U32("touchscreen-min-y",  8),
@@ -588,6 +623,22 @@ static const struct ts_dmi_data schneider_sct101ctm_data = {
        .properties     = schneider_sct101ctm_props,
 };
 
+static const struct property_entry techbite_arc_11_6_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-min-x", 5),
+       PROPERTY_ENTRY_U32("touchscreen-min-y", 7),
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1981),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1270),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-techbite-arc-11-6.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       { }
+};
+
+static const struct ts_dmi_data techbite_arc_11_6_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = techbite_arc_11_6_props,
+};
+
 static const struct property_entry teclast_x3_plus_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
@@ -662,11 +713,14 @@ static const struct ts_dmi_data trekstor_primetab_t13b_data = {
 };
 
 static const struct property_entry trekstor_surftab_twin_10_1_props[] = {
-       PROPERTY_ENTRY_U32("touchscreen-size-x", 1900),
+       PROPERTY_ENTRY_U32("touchscreen-min-x", 20),
+       PROPERTY_ENTRY_U32("touchscreen-min-y", 0),
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1890),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
        PROPERTY_ENTRY_U32("touchscreen-inverted-y", 1),
        PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-surftab-twin-10-1-st10432-8.fw"),
        PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
        { }
 };
 
@@ -691,6 +745,20 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = {
        .properties     = trekstor_surftab_wintron70_props,
 };
 
+static const struct property_entry vinga_twizzle_j116_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1920),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1280),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-vinga-twizzle_j116.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct ts_dmi_data vinga_twizzle_j116_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = vinga_twizzle_j116_props,
+};
+
 /* NOTE: Please keep this table sorted alphabetically */
 const struct dmi_system_id touchscreen_dmi_table[] = {
        {
@@ -908,6 +976,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
                },
        },
+       {
+               /* MP Man MPWIN895CL */
+               .driver_data = (void *)&mpman_mpwin895cl_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "MPMAN"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MPWIN8900CL"),
+               },
+       },
        {
                /* Myria MY8307 */
                .driver_data = (void *)&myria_my8307_data,
@@ -940,6 +1016,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "V820w DualOS")
                },
        },
+       {
+               /* ONDA V891 v5 */
+               .driver_data = (void *)&onda_v891_v5_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ONDA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "ONDA Tablet"),
+                       DMI_MATCH(DMI_BIOS_VERSION, "ONDA.D869CJABNRBA06"),
+               },
+       },
        {
                /* ONDA V891w revision P891WBEBV1B00 aka v1 */
                .driver_data = (void *)&onda_v891w_v1_data,
@@ -1029,6 +1114,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "SCT101CTM"),
                },
        },
+       {
+               /* Techbite Arc 11.6 */
+               .driver_data = (void *)&techbite_arc_11_6_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "mPTech"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "techBite Arc 11.6"),
+                       DMI_MATCH(DMI_BOARD_NAME, "G8316_272B"),
+               },
+       },
        {
                /* Teclast X3 Plus */
                .driver_data = (void *)&teclast_x3_plus_data,
@@ -1106,6 +1200,21 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA05"),
                },
        },
+       {
+               /* Trekstor Yourbook C11B (same touchscreen as the Primebook C11) */
+               .driver_data = (void *)&trekstor_primebook_c11_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "YOURBOOK C11B"),
+               },
+       },
+       {
+               /* Vinga Twizzle J116 */
+               .driver_data = (void *)&vinga_twizzle_j116_data,
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "VINGA Twizzle J116"),
+               },
+       },
        {
                /* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
                .driver_data = (void *)&chuwi_vi8_data,
@@ -1114,7 +1223,7 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
                },
        },
-       { },
+       { }
 };
 
 static const struct ts_dmi_data *ts_data;
index 941739db7199f1ed975f8a9ff9be50db104a9a33..d88f388a3450fa4bd61b5e4042ff3e30c71111d8 100644 (file)
@@ -111,11 +111,11 @@ static struct platform_driver acpi_wmi_driver = {
 
 static bool find_guid(const char *guid_string, struct wmi_block **out)
 {
-       uuid_le guid_input;
+       guid_t guid_input;
        struct wmi_block *wblock;
        struct guid_block *block;
 
-       if (uuid_le_to_bin(guid_string, &guid_input))
+       if (guid_parse(guid_string, &guid_input))
                return false;
 
        list_for_each_entry(wblock, &wmi_block_list, list) {
@@ -134,7 +134,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
                                      struct wmi_driver *wdriver)
 {
        const struct wmi_device_id *id;
-       uuid_le guid_input;
+       guid_t guid_input;
 
        if (wblock == NULL || wdriver == NULL)
                return NULL;
@@ -143,7 +143,7 @@ static const void *find_guid_context(struct wmi_block *wblock,
 
        id = wdriver->id_table;
        while (*id->guid_string) {
-               if (uuid_le_to_bin(id->guid_string, &guid_input))
+               if (guid_parse(id->guid_string, &guid_input))
                        continue;
                if (!memcmp(wblock->gblock.guid, &guid_input, 16))
                        return id->context;
@@ -202,7 +202,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
 /**
  * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
  * @wdev: A wmi bus device from a driver
- * @instance: Instance index
+ * @length: Required buffer size
  *
  * Allocates memory needed for buffer, stores the buffer size in that memory
  */
@@ -222,8 +222,8 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size);
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  * @instance: Instance index
  * @method_id: Method ID to call
- * &in: Buffer containing input for the method call
- * &out: Empty buffer to return the method results
+ * @in: Buffer containing input for the method call
+ * @out: Empty buffer to return the method results
  *
  * Call an ACPI-WMI method
  */
@@ -244,8 +244,8 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method);
  * @wdev: A wmi bus device from a driver
  * @instance: Instance index
  * @method_id: Method ID to call
- * &in: Buffer containing input for the method call
- * &out: Empty buffer to return the method results
+ * @in: Buffer containing input for the method call
+ * @out: Empty buffer to return the method results
  *
  * Call an ACPI-WMI method
  */
@@ -364,7 +364,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
  * wmi_query_block - Return contents of a WMI block (deprecated)
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  * @instance: Instance index
- * &out: Empty buffer to return the contents of the data block to
+ * @out: Empty buffer to return the contents of the data block to
  *
  * Return the contents of an ACPI-WMI data block to a buffer
  */
@@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(wmidev_block_query);
  * wmi_set_block - Write to a WMI block
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  * @instance: Instance index
- * &in: Buffer containing new values for the data block
+ * @in: Buffer containing new values for the data block
  *
  * Write the contents of the input buffer to an ACPI-WMI data block
  */
@@ -510,6 +510,7 @@ static void wmi_notify_debug(u32 value, void *context)
 
 /**
  * wmi_install_notify_handler - Register handler for WMI events
+ * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  * @handler: Function to handle notifications
  * @data: Data to be returned to handler when event is fired
  *
@@ -520,12 +521,12 @@ wmi_notify_handler handler, void *data)
 {
        struct wmi_block *block;
        acpi_status status = AE_NOT_EXIST;
-       uuid_le guid_input;
+       guid_t guid_input;
 
        if (!guid || !handler)
                return AE_BAD_PARAMETER;
 
-       if (uuid_le_to_bin(guid, &guid_input))
+       if (guid_parse(guid, &guid_input))
                return AE_BAD_PARAMETER;
 
        list_for_each_entry(block, &wmi_block_list, list) {
@@ -552,6 +553,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
 
 /**
  * wmi_uninstall_notify_handler - Unregister handler for WMI events
+ * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
  *
  * Unregister handler for events sent to the ACPI-WMI mapper device.
  */
@@ -559,12 +561,12 @@ acpi_status wmi_remove_notify_handler(const char *guid)
 {
        struct wmi_block *block;
        acpi_status status = AE_NOT_EXIST;
-       uuid_le guid_input;
+       guid_t guid_input;
 
        if (!guid)
                return AE_BAD_PARAMETER;
 
-       if (uuid_le_to_bin(guid, &guid_input))
+       if (guid_parse(guid, &guid_input))
                return AE_BAD_PARAMETER;
 
        list_for_each_entry(block, &wmi_block_list, list) {
@@ -795,9 +797,9 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
                return 0;
 
        while (*id->guid_string) {
-               uuid_le driver_guid;
+               guid_t driver_guid;
 
-               if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
+               if (WARN_ON(guid_parse(id->guid_string, &driver_guid)))
                        continue;
                if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
                        return 1;
@@ -1116,8 +1118,7 @@ static void wmi_free_devices(struct acpi_device *device)
        }
 }
 
-static bool guid_already_parsed(struct acpi_device *device,
-                               const u8 *guid)
+static bool guid_already_parsed(struct acpi_device *device, const u8 *guid)
 {
        struct wmi_block *wblock;
 
@@ -1327,10 +1328,8 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
                wblock->handler(event, wblock->handler_data);
        }
 
-       if (debug_event) {
-               pr_info("DEBUG Event GUID: %pUL\n",
-                       wblock->gblock.guid);
-       }
+       if (debug_event)
+               pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid);
 
        acpi_bus_generate_netlink_event(
                wblock->acpi_device->pnp.device_class,
index b8e1ec106627f1a59040461297846ec3e3b5d75d..3b820110ecfaba42cbd047962134eb3bb01b4f8f 100644 (file)
@@ -335,14 +335,14 @@ static int bd70528_get_present(struct bd70528_psy *bdpsy, int *val)
        return 0;
 }
 
-struct linear_range {
+struct bd70528_linear_range {
        int min;
        int step;
        int vals;
        int low_sel;
 };
 
-static const struct linear_range current_limit_ranges[] = {
+static const struct bd70528_linear_range current_limit_ranges[] = {
        {
                .min = 5,
                .step = 1,
@@ -374,7 +374,7 @@ static const struct linear_range current_limit_ranges[] = {
  * voltage for low temperatures. The driver currently only reads
  * the charge current at room temperature. We do set both though.
  */
-static const struct linear_range warm_charge_curr[] = {
+static const struct bd70528_linear_range warm_charge_curr[] = {
        {
                .min = 10,
                .step = 10,
@@ -398,7 +398,7 @@ static const struct linear_range warm_charge_curr[] = {
 #define MAX_WARM_CHG_CURR_SEL 0x1f
 #define MIN_CHG_CURR_SEL 0x0
 
-static int find_value_for_selector_low(const struct linear_range *r,
+static int find_value_for_selector_low(const struct bd70528_linear_range *r,
                                       int selectors, unsigned int sel,
                                       unsigned int *val)
 {
@@ -420,7 +420,7 @@ static int find_value_for_selector_low(const struct linear_range *r,
  * I guess it is enough if we use voltage/current which is closest (below)
  * the requested?
  */
-static int find_selector_for_value_low(const struct linear_range *r,
+static int find_selector_for_value_low(const struct bd70528_linear_range *r,
                                       int selectors, unsigned int val,
                                       unsigned int *sel, bool *found)
 {
index eb328655bc0186c5cb974cf99f0316ca368f55ed..61a63a16b5e7d068f375b7af6a3c3f46f0e61c2e 100644 (file)
@@ -26,9 +26,6 @@
 #include <asm/cpu_device_id.h>
 #include <asm/intel-family.h>
 
-/* Local defines */
-#define MSR_PLATFORM_POWER_LIMIT       0x0000065C
-
 /* bitmasks for RAPL MSRs, used by primitive access functions */
 #define ENERGY_STATUS_MASK      0xffffffff
 
@@ -989,6 +986,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT,       &rapl_defaults_core),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS,  &rapl_defaults_core),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D,     &rapl_defaults_core),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT,        &rapl_defaults_core),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      &rapl_defaults_core),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,      &rapl_defaults_core),
 
index d5ef55c81185438ad935a0bef7dcfcec85dc5036..71cfa2c5de5eea3ccb79ae67523cd9b8ae18d3bf 100644 (file)
@@ -11,13 +11,13 @@ static const struct regulator_ops pg86x_ops = {
        .list_voltage = regulator_list_voltage_linear_range,
 };
 
-static const struct regulator_linear_range pg86x_buck1_ranges[] = {
+static const struct linear_range pg86x_buck1_ranges[] = {
        REGULATOR_LINEAR_RANGE(      0,  0, 10,     0),
        REGULATOR_LINEAR_RANGE(1000000, 11, 34, 25000),
        REGULATOR_LINEAR_RANGE(1600000, 35, 47, 50000),
 };
 
-static const struct regulator_linear_range pg86x_buck2_ranges[] = {
+static const struct linear_range pg86x_buck2_ranges[] = {
        REGULATOR_LINEAR_RANGE(      0,  0, 15,     0),
        REGULATOR_LINEAR_RANGE(1000000, 16, 39, 25000),
        REGULATOR_LINEAR_RANGE(1600000, 40, 52, 50000),
index 69ae25886181fd2313a227f50065db14935e725d..d08ee81ed1ac59fdd7bc80b24f086d81516331cf 100644 (file)
@@ -134,13 +134,13 @@ struct pm800_regulator_info {
 }
 
 /* Ranges are sorted in ascending order. */
-static const struct regulator_linear_range buck1_volt_range[] = {
+static const struct linear_range buck1_volt_range[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500),
        REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x54, 50000),
 };
 
 /* BUCK 2~5 have same ranges. */
-static const struct regulator_linear_range buck2_5_volt_range[] = {
+static const struct linear_range buck2_5_volt_range[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 0x4f, 12500),
        REGULATOR_LINEAR_RANGE(1600000, 0x50, 0x72, 50000),
 };
index f4b72cb098efbef231d7263f5f8be790686f55a3..8f677f5d79b4dbe34b040bcc89e0a09e2b204f7e 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menuconfig REGULATOR
        bool "Voltage and Current Regulator Support"
+       select LINEAR_RANGES
        help
          Generic Voltage and Current Regulator support.
 
@@ -585,6 +586,16 @@ config REGULATOR_MAX77802
          Exynos5420/Exynos5800 SoCs to control various voltages.
          It includes support for control of voltage and ramp speed.
 
+config REGULATOR_MAX77826
+       tristate "Maxim 77826 regulator"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         This driver controls a Maxim 77826 regulator via I2C bus.
+         The regulator include 15 LDOs, BUCK and BUCK BOOST regulator.
+         It includes support for control of output voltage. This
+         regulator is found on the Samsung Galaxy S5 (klte) smartphone.
+
 config REGULATOR_MC13XXX_CORE
        tristate
 
index 6610ee001d9a1cd69b8dbbdf4957fe7102cd5839..e8f1633710711c8b39cd443a95152bcf5e469edc 100644 (file)
@@ -74,6 +74,7 @@ obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
 obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
 obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
 obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
+obj-$(CONFIG_REGULATOR_MAX77826) += max77826-regulator.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
index f60e1b26c2d2813720384010e8e8044467ea786d..716ca5bb178e75770bc3c2973149e7b21deb3f0c 100644 (file)
@@ -139,17 +139,6 @@ static const unsigned int ldo_vintcore_voltages[] = {
        1350000,
 };
 
-static const unsigned int ldo_sdio_voltages[] = {
-       1160000,
-       1050000,
-       1100000,
-       1500000,
-       1800000,
-       2200000,
-       2910000,
-       3050000,
-};
-
 static const unsigned int fixed_1200000_voltage[] = {
        1200000,
 };
@@ -166,10 +155,6 @@ static const unsigned int fixed_2050000_voltage[] = {
        2050000,
 };
 
-static const unsigned int fixed_3300000_voltage[] = {
-       3300000,
-};
-
 static const unsigned int ldo_vana_voltages[] = {
        1050000,
        1075000,
@@ -192,13 +177,6 @@ static const unsigned int ldo_vaudio_voltages[] = {
        2600000,        /* Duplicated in Vaudio and IsoUicc Control register. */
 };
 
-static const unsigned int ldo_vdmic_voltages[] = {
-       1800000,
-       1900000,
-       2000000,
-       2850000,
-};
-
 static DEFINE_MUTEX(shared_mode_mutex);
 static struct ab8500_shared_mode ldo_anamic1_shared;
 static struct ab8500_shared_mode ldo_anamic2_shared;
index 0fa97f934df49b0f3998fd7ee51287085ae9f2f5..19b9742c9eccaa21a5a43c9a90be60306a346fda 100644 (file)
@@ -220,13 +220,13 @@ static const struct regmap_config act8865_regmap_config = {
        .val_bits = 8,
 };
 
-static const struct regulator_linear_range act8865_voltage_ranges[] = {
+static const struct linear_range act8865_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),
        REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),
        REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
 };
 
-static const struct regulator_linear_range act8600_sudcdc_voltage_ranges[] = {
+static const struct linear_range act8600_sudcdc_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(3000000, 0, 63, 0),
        REGULATOR_LINEAR_RANGE(3000000, 64, 159, 100000),
        REGULATOR_LINEAR_RANGE(12600000, 160, 191, 200000),
index d2f804dbc785da98d871984c1e3c7e392426b46b..6a62f946ccae44e5fbb3855a60eec9f9cdeb7a95 100644 (file)
@@ -73,7 +73,7 @@ struct act8945a_pmic {
        u32 op_mode[ACT8945A_ID_MAX];
 };
 
-static const struct regulator_linear_range act8945a_voltage_ranges[] = {
+static const struct linear_range act8945a_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),
        REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),
        REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
index 1a3d7b720f5e0a862bfd9c882d6a0ccd2804de35..ade0bef4569d977d3fb235c9d8ab688dd4cf27ad 100644 (file)
@@ -87,7 +87,7 @@ static const struct regulator_ops arizona_ldo1_hc_ops = {
        .set_bypass = regulator_set_bypass_regmap,
 };
 
-static const struct regulator_linear_range arizona_ldo1_hc_ranges[] = {
+static const struct linear_range arizona_ldo1_hc_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),
        REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),
 };
index ae1a5de3e57d5f976961108703a6901402a3bf5a..f6cfd3f6f0dd323974dc9524390a0f9f2842100f 100644 (file)
@@ -125,7 +125,7 @@ static const struct regulator_ops arizona_micsupp_ops = {
        .set_bypass = arizona_micsupp_set_bypass,
 };
 
-static const struct regulator_linear_range arizona_micsupp_ranges[] = {
+static const struct linear_range arizona_micsupp_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0,    0x1e, 50000),
        REGULATOR_LINEAR_RANGE(3300000, 0x1f, 0x1f, 0),
 };
@@ -152,7 +152,7 @@ static const struct regulator_desc arizona_micsupp = {
        .owner = THIS_MODULE,
 };
 
-static const struct regulator_linear_range arizona_micsupp_ext_ranges[] = {
+static const struct linear_range arizona_micsupp_ext_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
        REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
 };
index ece88103f2fdb5d5a99ae020ea2d7848b038b61b..b6b9206969ae1cdfeb886f13dbd52c8b1df8a62f 100644 (file)
@@ -103,18 +103,18 @@ static const struct regulator_ops as3711_dldo_ops = {
        .map_voltage            = regulator_map_voltage_linear_range,
 };
 
-static const struct regulator_linear_range as3711_sd_ranges[] = {
+static const struct linear_range as3711_sd_ranges[] = {
        REGULATOR_LINEAR_RANGE(612500, 0x1, 0x40, 12500),
        REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000),
        REGULATOR_LINEAR_RANGE(2650000, 0x71, 0x7f, 50000),
 };
 
-static const struct regulator_linear_range as3711_aldo_ranges[] = {
+static const struct linear_range as3711_aldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(1200000, 0, 0xf, 50000),
        REGULATOR_LINEAR_RANGE(1800000, 0x10, 0x1f, 100000),
 };
 
-static const struct regulator_linear_range as3711_dldo_ranges[] = {
+static const struct linear_range as3711_dldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 0x10, 50000),
        REGULATOR_LINEAR_RANGE(1750000, 0x20, 0x3f, 50000),
 };
index bd5d0bacb08dbf252952d969de826f80d63a24ee..33ca197860b398ef956ca3e23b278d0da502dede 100644 (file)
@@ -389,7 +389,7 @@ static const struct regulator_ops as3722_ldo6_extcntrl_ops = {
        .set_bypass = regulator_set_bypass_regmap,
 };
 
-static const struct regulator_linear_range as3722_ldo_ranges[] = {
+static const struct linear_range as3722_ldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x00, 0x00, 0),
        REGULATOR_LINEAR_RANGE(825000, 0x01, 0x24, 25000),
        REGULATOR_LINEAR_RANGE(1725000, 0x40, 0x7F, 25000),
@@ -487,7 +487,7 @@ static bool as3722_sd0_is_low_voltage(struct as3722_regulators *as3722_regs)
        return false;
 }
 
-static const struct regulator_linear_range as3722_sd2345_ranges[] = {
+static const struct linear_range as3722_sd2345_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x00, 0x00, 0),
        REGULATOR_LINEAR_RANGE(612500, 0x01, 0x40, 12500),
        REGULATOR_LINEAR_RANGE(1425000, 0x41, 0x70, 25000),
index 1e6eb5b1f8d8527bd7bdcfb893f0729601130cf7..fbc95cadaf5393d4cb27dfe7bdeaf31895c75e2d 100644 (file)
@@ -510,7 +510,7 @@ static const struct regulator_ops axp20x_ops_sw = {
        .is_enabled             = regulator_is_enabled_regmap,
 };
 
-static const struct regulator_linear_range axp20x_ldo4_ranges[] = {
+static const struct linear_range axp20x_ldo4_ranges[] = {
        REGULATOR_LINEAR_RANGE(1250000,
                               AXP20X_LDO4_V_OUT_1250mV_START,
                               AXP20X_LDO4_V_OUT_1250mV_END,
@@ -638,7 +638,7 @@ static const struct regulator_desc axp22x_drivevbus_regulator = {
 };
 
 /* DCDC ranges shared with AXP813 */
-static const struct regulator_linear_range axp803_dcdc234_ranges[] = {
+static const struct linear_range axp803_dcdc234_ranges[] = {
        REGULATOR_LINEAR_RANGE(500000,
                               AXP803_DCDC234_500mV_START,
                               AXP803_DCDC234_500mV_END,
@@ -649,7 +649,7 @@ static const struct regulator_linear_range axp803_dcdc234_ranges[] = {
                               20000),
 };
 
-static const struct regulator_linear_range axp803_dcdc5_ranges[] = {
+static const struct linear_range axp803_dcdc5_ranges[] = {
        REGULATOR_LINEAR_RANGE(800000,
                               AXP803_DCDC5_800mV_START,
                               AXP803_DCDC5_800mV_END,
@@ -660,7 +660,7 @@ static const struct regulator_linear_range axp803_dcdc5_ranges[] = {
                               20000),
 };
 
-static const struct regulator_linear_range axp803_dcdc6_ranges[] = {
+static const struct linear_range axp803_dcdc6_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000,
                               AXP803_DCDC6_600mV_START,
                               AXP803_DCDC6_600mV_END,
@@ -672,7 +672,7 @@ static const struct regulator_linear_range axp803_dcdc6_ranges[] = {
 };
 
 /* AXP806's CLDO2 and AXP809's DLDO1 share the same range */
-static const struct regulator_linear_range axp803_dldo2_ranges[] = {
+static const struct linear_range axp803_dldo2_ranges[] = {
        REGULATOR_LINEAR_RANGE(700000,
                               AXP803_DLDO2_700mV_START,
                               AXP803_DLDO2_700mV_END,
@@ -758,7 +758,7 @@ static const struct regulator_desc axp803_regulators[] = {
        AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000),
 };
 
-static const struct regulator_linear_range axp806_dcdca_ranges[] = {
+static const struct linear_range axp806_dcdca_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000,
                               AXP806_DCDCA_600mV_START,
                               AXP806_DCDCA_600mV_END,
@@ -769,7 +769,7 @@ static const struct regulator_linear_range axp806_dcdca_ranges[] = {
                               20000),
 };
 
-static const struct regulator_linear_range axp806_dcdcd_ranges[] = {
+static const struct linear_range axp806_dcdcd_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000,
                               AXP806_DCDCD_600mV_START,
                               AXP806_DCDCD_600mV_END,
@@ -834,7 +834,7 @@ static const struct regulator_desc axp806_regulators[] = {
                    AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK),
 };
 
-static const struct regulator_linear_range axp809_dcdc4_ranges[] = {
+static const struct linear_range axp809_dcdc4_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000,
                               AXP809_DCDC4_600mV_START,
                               AXP809_DCDC4_600mV_END,
index 8c98c3f07660839a8cefb66d52ebf14ddcf81ce8..65e23fc5f9c399fa15145c5534f57163decbeef4 100644 (file)
@@ -116,14 +116,14 @@ static const unsigned int ldo_vbus[] = {
 };
 
 /* DCDC group CSR: supported voltages in microvolts */
-static const struct regulator_linear_range dcdc_csr_ranges[] = {
+static const struct linear_range dcdc_csr_ranges[] = {
        REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000),
        REGULATOR_LINEAR_RANGE(1360000, 51, 55, 20000),
        REGULATOR_LINEAR_RANGE(900000, 56, 63, 0),
 };
 
 /* DCDC group IOSR1: supported voltages in microvolts */
-static const struct regulator_linear_range dcdc_iosr1_ranges[] = {
+static const struct linear_range dcdc_iosr1_ranges[] = {
        REGULATOR_LINEAR_RANGE(860000, 2, 51, 10000),
        REGULATOR_LINEAR_RANGE(1500000, 52, 52, 0),
        REGULATOR_LINEAR_RANGE(1800000, 53, 53, 0),
@@ -131,7 +131,7 @@ static const struct regulator_linear_range dcdc_iosr1_ranges[] = {
 };
 
 /* DCDC group SDSR1: supported voltages in microvolts */
-static const struct regulator_linear_range dcdc_sdsr1_ranges[] = {
+static const struct linear_range dcdc_sdsr1_ranges[] = {
        REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000),
        REGULATOR_LINEAR_RANGE(1340000, 51, 51, 0),
        REGULATOR_LINEAR_RANGE(900000, 52, 63, 0),
@@ -143,7 +143,7 @@ struct bcm590xx_info {
        u8 n_voltages;
        const unsigned int *volt_table;
        u8 n_linear_ranges;
-       const struct regulator_linear_range *linear_ranges;
+       const struct linear_range *linear_ranges;
 };
 
 #define BCM590XX_REG_TABLE(_name, _table) \
index 5bf8a2dc5fe778302d3e79525e1ef4d4f70f3282..d44adf7e875a960190ae64145a8b792f5aef9ce1 100644 (file)
 #define BUCK_RAMPRATE_125MV 1
 #define BUCK_RAMP_MAX 250
 
-static const struct regulator_linear_range bd70528_buck1_volts[] = {
+static const struct linear_range bd70528_buck1_volts[] = {
        REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x1, 600000),
        REGULATOR_LINEAR_RANGE(2750000, 0x2, 0xf, 50000),
 };
-static const struct regulator_linear_range bd70528_buck2_volts[] = {
+static const struct linear_range bd70528_buck2_volts[] = {
        REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x1, 300000),
        REGULATOR_LINEAR_RANGE(1550000, 0x2, 0xd, 50000),
        REGULATOR_LINEAR_RANGE(3000000, 0xe, 0xf, 300000),
 };
-static const struct regulator_linear_range bd70528_buck3_volts[] = {
+static const struct linear_range bd70528_buck3_volts[] = {
        REGULATOR_LINEAR_RANGE(800000, 0x00, 0xd, 50000),
        REGULATOR_LINEAR_RANGE(1800000, 0xe, 0xf, 0),
 };
 
 /* All LDOs have same voltage ranges */
-static const struct regulator_linear_range bd70528_ldo_volts[] = {
+static const struct linear_range bd70528_ldo_volts[] = {
        REGULATOR_LINEAR_RANGE(1650000, 0x0, 0x07, 50000),
        REGULATOR_LINEAR_RANGE(2100000, 0x8, 0x0f, 100000),
        REGULATOR_LINEAR_RANGE(2850000, 0x10, 0x19, 50000),
index b2fa17be49882df9874963d42a424a84ee064509..85c0b900096393332c817b3ff46d4f9b0391a9a6 100644 (file)
@@ -65,27 +65,27 @@ static const struct reg_init buck7_inits[] = {
        },
 };
 
-static const struct regulator_linear_range bd71828_buck1267_volts[] = {
+static const struct linear_range bd71828_buck1267_volts[] = {
        REGULATOR_LINEAR_RANGE(500000, 0x00, 0xef, 6250),
        REGULATOR_LINEAR_RANGE(2000000, 0xf0, 0xff, 0),
 };
 
-static const struct regulator_linear_range bd71828_buck3_volts[] = {
+static const struct linear_range bd71828_buck3_volts[] = {
        REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x0f, 50000),
        REGULATOR_LINEAR_RANGE(2000000, 0x10, 0x1f, 0),
 };
 
-static const struct regulator_linear_range bd71828_buck4_volts[] = {
+static const struct linear_range bd71828_buck4_volts[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0x00, 0x1f, 25000),
        REGULATOR_LINEAR_RANGE(1800000, 0x20, 0x3f, 0),
 };
 
-static const struct regulator_linear_range bd71828_buck5_volts[] = {
+static const struct linear_range bd71828_buck5_volts[] = {
        REGULATOR_LINEAR_RANGE(2500000, 0x00, 0x0f, 50000),
        REGULATOR_LINEAR_RANGE(3300000, 0x10, 0x1f, 0),
 };
 
-static const struct regulator_linear_range bd71828_ldo_volts[] = {
+static const struct linear_range bd71828_ldo_volts[] = {
        REGULATOR_LINEAR_RANGE(800000, 0x00, 0x31, 50000),
        REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0),
 };
index cf3872837abcc9f72c383c43fe6caacba1e3b029..7b311389f925418c00919a58e9fe49259e057f66 100644 (file)
@@ -55,12 +55,20 @@ static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev,
                                  BUCK_RAMPRATE_MASK, ramp_value << 6);
 }
 
-/* Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed.
+/*
+ * On BD71837 (not on BD71847, BD71850, ...)
+ * Bucks 1 to 4 support DVS. PWM mode is used when voltage is changed.
  * Bucks 5 to 8 and LDOs can use PFM and must be disabled when voltage
  * is changed. Hence we return -EBUSY for these if voltage is changed
  * when BUCK/LDO is enabled.
+ *
+ * On BD71847, BD71850, ... The LDO voltage can be changed when LDO is
+ * enabled. But if voltage is increased the LDO power-good monitoring
+ * must be disabled for the duration of changing + 1mS to ensure voltage
+ * has reached the higher level before HW does next under voltage detection
+ * cycle.
  */
-static int bd718xx_set_voltage_sel_restricted(struct regulator_dev *rdev,
+static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
                                                    unsigned int sel)
 {
        if (regulator_is_enabled_regmap(rdev))
@@ -69,8 +77,123 @@ static int bd718xx_set_voltage_sel_restricted(struct regulator_dev *rdev,
        return regulator_set_voltage_sel_regmap(rdev, sel);
 }
 
+static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel,
+                               unsigned int *mask)
+{
+       int ret;
+
+       if (*mask) {
+               /*
+                * Let's allow scheduling as we use I2C anyways. We just need to
+                * guarantee minimum of 1ms sleep - it shouldn't matter if we
+                * exceed it due to the scheduling.
+                */
+               msleep(1);
+               /*
+                * Note for next hacker. The PWRGOOD should not be masked on
+                * BD71847 so we will just unconditionally enable detection
+                * when voltage is set.
+                * If someone want's to disable PWRGOOD he must implement
+                * caching and restoring the old value here. I am not
+                * aware of such use-cases so for the sake of the simplicity
+                * we just always enable PWRGOOD here.
+                */
+               ret = regmap_update_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
+                                        *mask, 0);
+               if (ret)
+                       dev_err(&rdev->dev,
+                               "Failed to re-enable voltage monitoring (%d)\n",
+                               ret);
+       }
+}
+
+static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel,
+                                 unsigned int *mask)
+{
+       int ret;
+
+       *mask = 0;
+       if (regulator_is_enabled_regmap(rdev)) {
+               int now, new;
+
+               now = rdev->desc->ops->get_voltage_sel(rdev);
+               if (now < 0)
+                       return now;
+
+               now = rdev->desc->ops->list_voltage(rdev, now);
+               if (now < 0)
+                       return now;
+
+               new = rdev->desc->ops->list_voltage(rdev, sel);
+               if (new < 0)
+                       return new;
+
+               /*
+                * If we increase LDO voltage when LDO is enabled we need to
+                * disable the power-good detection until voltage has reached
+                * the new level. According to HW colleagues the maximum time
+                * it takes is 1000us. I assume that on systems with light load
+                * this might be less - and we could probably use DT to give
+                * system specific delay value if performance matters.
+                *
+                * Well, knowing we use I2C here and can add scheduling delays
+                * I don't think it is worth the hassle and I just add fixed
+                * 1ms sleep here (and allow scheduling). If this turns out to
+                * be a problem we can change it to delay and make the delay
+                * time configurable.
+                */
+               if (new > now) {
+                       int ldo_offset = rdev->desc->id - BD718XX_LDO1;
+
+                       *mask = BD718XX_LDO1_VRMON80 << ldo_offset;
+                       ret = regmap_update_bits(rdev->regmap,
+                                                BD718XX_REG_MVRFLTMASK2,
+                                                *mask, *mask);
+                       if (ret) {
+                               dev_err(&rdev->dev,
+                                       "Failed to stop voltage monitoring\n");
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int bd718xx_set_voltage_sel_restricted(struct regulator_dev *rdev,
+                                                   unsigned int sel)
+{
+       int ret;
+       int mask;
+
+       ret = voltage_change_prepare(rdev, sel, &mask);
+       if (ret)
+               return ret;
+
+       ret = regulator_set_voltage_sel_regmap(rdev, sel);
+       voltage_change_done(rdev, sel, &mask);
+
+       return ret;
+}
+
 static int bd718xx_set_voltage_sel_pickable_restricted(
                struct regulator_dev *rdev, unsigned int sel)
+{
+       int ret;
+       int mask;
+
+       ret = voltage_change_prepare(rdev, sel, &mask);
+       if (ret)
+               return ret;
+
+       ret = regulator_set_voltage_sel_pickable_regmap(rdev, sel);
+       voltage_change_done(rdev, sel, &mask);
+
+       return ret;
+}
+
+static int bd71837_set_voltage_sel_pickable_restricted(
+               struct regulator_dev *rdev, unsigned int sel)
 {
        if (regulator_is_enabled_regmap(rdev))
                return -EBUSY;
@@ -85,6 +208,16 @@ static const struct regulator_ops bd718xx_pickable_range_ldo_ops = {
        .list_voltage = regulator_list_voltage_pickable_linear_range,
        .set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted,
        .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
+
+};
+
+static const struct regulator_ops bd71837_pickable_range_ldo_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_pickable_linear_range,
+       .set_voltage_sel = bd71837_set_voltage_sel_pickable_restricted,
+       .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
 };
 
 static const struct regulator_ops bd718xx_pickable_range_buck_ops = {
@@ -92,11 +225,30 @@ static const struct regulator_ops bd718xx_pickable_range_buck_ops = {
        .disable = regulator_disable_regmap,
        .is_enabled = regulator_is_enabled_regmap,
        .list_voltage = regulator_list_voltage_pickable_linear_range,
-       .set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted,
+       .set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static const struct regulator_ops bd71837_pickable_range_buck_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_pickable_linear_range,
+       .set_voltage_sel = bd71837_set_voltage_sel_pickable_restricted,
        .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
        .set_voltage_time_sel = regulator_set_voltage_time_sel,
 };
 
+static const struct regulator_ops bd71837_ldo_regulator_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_linear_range,
+       .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
 static const struct regulator_ops bd718xx_ldo_regulator_ops = {
        .enable = regulator_enable_regmap,
        .disable = regulator_disable_regmap,
@@ -106,6 +258,15 @@ static const struct regulator_ops bd718xx_ldo_regulator_ops = {
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
 };
 
+static const struct regulator_ops bd71837_ldo_regulator_nolinear_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_table,
+       .set_voltage_sel = bd71837_set_voltage_sel_restricted,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
 static const struct regulator_ops bd718xx_ldo_regulator_nolinear_ops = {
        .enable = regulator_enable_regmap,
        .disable = regulator_disable_regmap,
@@ -120,12 +281,33 @@ static const struct regulator_ops bd718xx_buck_regulator_ops = {
        .disable = regulator_disable_regmap,
        .is_enabled = regulator_is_enabled_regmap,
        .list_voltage = regulator_list_voltage_linear_range,
-       .set_voltage_sel = bd718xx_set_voltage_sel_restricted,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static const struct regulator_ops bd71837_buck_regulator_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_linear_range,
+       .set_voltage_sel = bd71837_set_voltage_sel_restricted,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_time_sel = regulator_set_voltage_time_sel,
 };
 
 static const struct regulator_ops bd718xx_buck_regulator_nolinear_ops = {
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .list_voltage = regulator_list_voltage_table,
+       .map_voltage = regulator_map_voltage_ascend,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+static const struct regulator_ops bd71837_buck_regulator_nolinear_ops = {
        .enable = regulator_enable_regmap,
        .disable = regulator_disable_regmap,
        .is_enabled = regulator_is_enabled_regmap,
@@ -152,7 +334,7 @@ static const struct regulator_ops bd718xx_dvs_buck_regulator_ops = {
  * BD71847 BUCK1/2
  * 0.70 to 1.30V (10mV step)
  */
-static const struct regulator_linear_range bd718xx_dvs_buck_volts[] = {
+static const struct linear_range bd718xx_dvs_buck_volts[] = {
        REGULATOR_LINEAR_RANGE(700000, 0x00, 0x3C, 10000),
        REGULATOR_LINEAR_RANGE(1300000, 0x3D, 0x3F, 0),
 };
@@ -163,7 +345,7 @@ static const struct regulator_linear_range bd718xx_dvs_buck_volts[] = {
  * and
  * 0.675 to 1.325 (range 1)
  */
-static const struct regulator_linear_range bd71837_buck5_volts[] = {
+static const struct linear_range bd71837_buck5_volts[] = {
        /* Ranges when VOLT_SEL bit is 0 */
        REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
        REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
@@ -185,7 +367,7 @@ static const unsigned int bd71837_buck5_volt_range_sel[] = {
 /*
  * BD71847 BUCK3
  */
-static const struct regulator_linear_range bd71847_buck3_volts[] = {
+static const struct linear_range bd71847_buck3_volts[] = {
        /* Ranges when VOLT_SEL bits are 00 */
        REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
        REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
@@ -202,7 +384,7 @@ static const unsigned int bd71847_buck3_volt_range_sel[] = {
        0x0, 0x0, 0x0, 0x40, 0x80, 0x80, 0x80
 };
 
-static const struct regulator_linear_range bd71847_buck4_volts[] = {
+static const struct linear_range bd71847_buck4_volts[] = {
        REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
        REGULATOR_LINEAR_RANGE(2600000, 0x00, 0x03, 100000),
 };
@@ -213,7 +395,7 @@ static const unsigned int bd71847_buck4_volt_range_sel[] = { 0x0, 0x40 };
  * BUCK6
  * 3.0V to 3.3V (step 100mV)
  */
-static const struct regulator_linear_range bd71837_buck6_volts[] = {
+static const struct linear_range bd71837_buck6_volts[] = {
        REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
 };
 
@@ -237,7 +419,7 @@ static const unsigned int bd718xx_3rd_nodvs_buck_volts[] = {
  * BUCK8
  * 0.8V to 1.40V (step 10mV)
  */
-static const struct regulator_linear_range bd718xx_4th_nodvs_buck_volts[] = {
+static const struct linear_range bd718xx_4th_nodvs_buck_volts[] = {
        REGULATOR_LINEAR_RANGE(800000, 0x00, 0x3C, 10000),
 };
 
@@ -245,7 +427,7 @@ static const struct regulator_linear_range bd718xx_4th_nodvs_buck_volts[] = {
  * LDO1
  * 3.0 to 3.3V (100mV step)
  */
-static const struct regulator_linear_range bd718xx_ldo1_volts[] = {
+static const struct linear_range bd718xx_ldo1_volts[] = {
        REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
        REGULATOR_LINEAR_RANGE(1600000, 0x00, 0x03, 100000),
 };
@@ -264,7 +446,7 @@ static const unsigned int ldo_2_volts[] = {
  * LDO3
  * 1.8 to 3.3V (100mV step)
  */
-static const struct regulator_linear_range bd718xx_ldo3_volts[] = {
+static const struct linear_range bd718xx_ldo3_volts[] = {
        REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
 };
 
@@ -272,7 +454,7 @@ static const struct regulator_linear_range bd718xx_ldo3_volts[] = {
  * LDO4
  * 0.9 to 1.8V (100mV step)
  */
-static const struct regulator_linear_range bd718xx_ldo4_volts[] = {
+static const struct linear_range bd718xx_ldo4_volts[] = {
        REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
 };
 
@@ -280,7 +462,7 @@ static const struct regulator_linear_range bd718xx_ldo4_volts[] = {
  * LDO5 for BD71837
  * 1.8 to 3.3V (100mV step)
  */
-static const struct regulator_linear_range bd71837_ldo5_volts[] = {
+static const struct linear_range bd71837_ldo5_volts[] = {
        REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
 };
 
@@ -288,7 +470,7 @@ static const struct regulator_linear_range bd71837_ldo5_volts[] = {
  * LDO5 for BD71837
  * 1.8 to 3.3V (100mV step)
  */
-static const struct regulator_linear_range bd71847_ldo5_volts[] = {
+static const struct linear_range bd71847_ldo5_volts[] = {
        REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
        REGULATOR_LINEAR_RANGE(800000, 0x00, 0x0F, 100000),
 };
@@ -299,7 +481,7 @@ static const unsigned int bd71847_ldo5_volt_range_sel[] = { 0x0, 0x20 };
  * LDO6
  * 0.9 to 1.8V (100mV step)
  */
-static const struct regulator_linear_range bd718xx_ldo6_volts[] = {
+static const struct linear_range bd718xx_ldo6_volts[] = {
        REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
 };
 
@@ -307,7 +489,7 @@ static const struct regulator_linear_range bd718xx_ldo6_volts[] = {
  * LDO7
  * 1.8 to 3.3V (100mV step)
  */
-static const struct regulator_linear_range bd71837_ldo7_volts[] = {
+static const struct linear_range bd71837_ldo7_volts[] = {
        REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
 };
 
@@ -805,7 +987,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("BUCK5"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_BUCK5,
-                       .ops = &bd718xx_pickable_range_buck_ops,
+                       .ops = &bd71837_pickable_range_buck_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD71837_BUCK5_VOLTAGE_NUM,
                        .linear_ranges = bd71837_buck5_volts,
@@ -832,7 +1014,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("BUCK6"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_BUCK6,
-                       .ops = &bd718xx_buck_regulator_ops,
+                       .ops = &bd71837_buck_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD71837_BUCK6_VOLTAGE_NUM,
                        .linear_ranges = bd71837_buck6_volts,
@@ -856,7 +1038,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("BUCK7"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_BUCK7,
-                       .ops = &bd718xx_buck_regulator_nolinear_ops,
+                       .ops = &bd71837_buck_regulator_nolinear_ops,
                        .type = REGULATOR_VOLTAGE,
                        .volt_table = &bd718xx_3rd_nodvs_buck_volts[0],
                        .n_voltages = ARRAY_SIZE(bd718xx_3rd_nodvs_buck_volts),
@@ -878,7 +1060,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("BUCK8"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_BUCK8,
-                       .ops = &bd718xx_buck_regulator_ops,
+                       .ops = &bd71837_buck_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD718XX_4TH_NODVS_BUCK_VOLTAGE_NUM,
                        .linear_ranges = bd718xx_4th_nodvs_buck_volts,
@@ -902,7 +1084,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO1"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO1,
-                       .ops = &bd718xx_pickable_range_ldo_ops,
+                       .ops = &bd71837_pickable_range_ldo_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD718XX_LDO1_VOLTAGE_NUM,
                        .linear_ranges = bd718xx_ldo1_volts,
@@ -928,7 +1110,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO2"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO2,
-                       .ops = &bd718xx_ldo_regulator_nolinear_ops,
+                       .ops = &bd71837_ldo_regulator_nolinear_ops,
                        .type = REGULATOR_VOLTAGE,
                        .volt_table = &ldo_2_volts[0],
                        .vsel_reg = BD718XX_REG_LDO2_VOLT,
@@ -950,7 +1132,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO3"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO3,
-                       .ops = &bd718xx_ldo_regulator_ops,
+                       .ops = &bd71837_ldo_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD718XX_LDO3_VOLTAGE_NUM,
                        .linear_ranges = bd718xx_ldo3_volts,
@@ -973,7 +1155,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO4"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO4,
-                       .ops = &bd718xx_ldo_regulator_ops,
+                       .ops = &bd71837_ldo_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD718XX_LDO4_VOLTAGE_NUM,
                        .linear_ranges = bd718xx_ldo4_volts,
@@ -996,7 +1178,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO5"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO5,
-                       .ops = &bd718xx_ldo_regulator_ops,
+                       .ops = &bd71837_ldo_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD71837_LDO5_VOLTAGE_NUM,
                        .linear_ranges = bd71837_ldo5_volts,
@@ -1023,7 +1205,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO6"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO6,
-                       .ops = &bd718xx_ldo_regulator_ops,
+                       .ops = &bd71837_ldo_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD718XX_LDO6_VOLTAGE_NUM,
                        .linear_ranges = bd718xx_ldo6_volts,
@@ -1050,7 +1232,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
                        .of_match = of_match_ptr("LDO7"),
                        .regulators_node = of_match_ptr("regulators"),
                        .id = BD718XX_LDO7,
-                       .ops = &bd718xx_ldo_regulator_ops,
+                       .ops = &bd71837_ldo_regulator_ops,
                        .type = REGULATOR_VOLTAGE,
                        .n_voltages = BD71837_LDO7_VOLTAGE_NUM,
                        .linear_ranges = bd71837_ldo7_volts,
index 7486f6e4e613ce15e2df9bdd05503dc47ab0dd93..03154f5b939f72e54a086bcf283de14e222ed998 100644 (file)
@@ -3642,36 +3642,19 @@ finish:
        return done;
 }
 
-static int regulator_balance_voltage(struct regulator_dev *rdev,
-                                    suspend_state_t state)
+int regulator_do_balance_voltage(struct regulator_dev *rdev,
+                                suspend_state_t state, bool skip_coupled)
 {
        struct regulator_dev **c_rdevs;
        struct regulator_dev *best_rdev;
        struct coupling_desc *c_desc = &rdev->coupling_desc;
-       struct regulator_coupler *coupler = c_desc->coupler;
        int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;
        unsigned int delta, best_delta;
        unsigned long c_rdev_done = 0;
        bool best_c_rdev_done;
 
        c_rdevs = c_desc->coupled_rdevs;
-       n_coupled = c_desc->n_coupled;
-
-       /*
-        * If system is in a state other than PM_SUSPEND_ON, don't check
-        * other coupled regulators.
-        */
-       if (state != PM_SUSPEND_ON)
-               n_coupled = 1;
-
-       if (c_desc->n_resolved < n_coupled) {
-               rdev_err(rdev, "Not all coupled regulators registered\n");
-               return -EPERM;
-       }
-
-       /* Invoke custom balancer for customized couplers */
-       if (coupler && coupler->balance_voltage)
-               return coupler->balance_voltage(coupler, rdev, state);
+       n_coupled = skip_coupled ? 1 : c_desc->n_coupled;
 
        /*
         * Find the best possible voltage change on each loop. Leave the loop
@@ -3742,6 +3725,32 @@ out:
        return ret;
 }
 
+static int regulator_balance_voltage(struct regulator_dev *rdev,
+                                    suspend_state_t state)
+{
+       struct coupling_desc *c_desc = &rdev->coupling_desc;
+       struct regulator_coupler *coupler = c_desc->coupler;
+       bool skip_coupled = false;
+
+       /*
+        * If system is in a state other than PM_SUSPEND_ON, don't check
+        * other coupled regulators.
+        */
+       if (state != PM_SUSPEND_ON)
+               skip_coupled = true;
+
+       if (c_desc->n_resolved < c_desc->n_coupled) {
+               rdev_err(rdev, "Not all coupled regulators registered\n");
+               return -EPERM;
+       }
+
+       /* Invoke custom balancer for customized couplers */
+       if (coupler && coupler->balance_voltage)
+               return coupler->balance_voltage(coupler, rdev, state);
+
+       return regulator_do_balance_voltage(rdev, state, skip_coupled);
+}
+
 /**
  * regulator_set_voltage - set regulator output voltage
  * @regulator: regulator source
@@ -4312,6 +4321,7 @@ EXPORT_SYMBOL_GPL(regulator_set_load);
 int regulator_allow_bypass(struct regulator *regulator, bool enable)
 {
        struct regulator_dev *rdev = regulator->rdev;
+       const char *name = rdev_get_name(rdev);
        int ret = 0;
 
        if (!rdev->desc->ops->set_bypass)
@@ -4326,18 +4336,26 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
                rdev->bypass_count++;
 
                if (rdev->bypass_count == rdev->open_count) {
+                       trace_regulator_bypass_enable(name);
+
                        ret = rdev->desc->ops->set_bypass(rdev, enable);
                        if (ret != 0)
                                rdev->bypass_count--;
+                       else
+                               trace_regulator_bypass_enable_complete(name);
                }
 
        } else if (!enable && regulator->bypass) {
                rdev->bypass_count--;
 
                if (rdev->bypass_count != rdev->open_count) {
+                       trace_regulator_bypass_disable(name);
+
                        ret = rdev->desc->ops->set_bypass(rdev, enable);
                        if (ret != 0)
                                rdev->bypass_count++;
+                       else
+                               trace_regulator_bypass_disable_complete(name);
                }
        }
 
@@ -5496,6 +5514,7 @@ static void regulator_summary_show_subtree(struct seq_file *s,
                seq_printf(s, "%*s%-*s ",
                           (level + 1) * 3 + 1, "",
                           30 - (level + 1) * 3,
+                          consumer->supply_name ? consumer->supply_name :
                           consumer->dev ? dev_name(consumer->dev) : "deviceless");
 
                switch (rdev->desc->type) {
index 5493c3a8642678fab79911531d8b12d63c7773cd..770e694824ac5e9d6d51150393e3faa41563e502 100644 (file)
@@ -248,7 +248,7 @@ static int da9034_set_dvc_voltage_sel(struct regulator_dev *rdev,
        return ret;
 }
 
-static const struct regulator_linear_range da9034_ldo12_ranges[] = {
+static const struct linear_range da9034_ldo12_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0, 7, 50000),
        REGULATOR_LINEAR_RANGE(2700000, 8, 15, 50000),
 };
index 88a2dcb9fe8ad3a78c5f82abb783077fdf65897c..0ce6ec4933af357bcd14dac88c7cb87dcb1ccdac 100644 (file)
@@ -181,7 +181,7 @@ static int db8500_regulator_switch_disable(struct regulator_dev *rdev)
                goto out;
        }
 
-       info->is_enabled = 0;
+       info->is_enabled = false;
 out:
        return ret;
 }
index bb16c465426ef80ad228a4f6efe7b86bc06ef10b..e970e9d2f8be077f0c31fed28f25999fca5cafb3 100644 (file)
@@ -131,10 +131,11 @@ int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
        unsigned int r_val;
        int range;
        unsigned int val;
-       int ret, i;
-       unsigned int voltages_in_range = 0;
+       int ret;
+       unsigned int voltages = 0;
+       const struct linear_range *r = rdev->desc->linear_ranges;
 
-       if (!rdev->desc->linear_ranges)
+       if (!r)
                return -EINVAL;
 
        ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
@@ -152,11 +153,9 @@ int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
        if (range < 0)
                return -EINVAL;
 
-       for (i = 0; i < range; i++)
-               voltages_in_range += (rdev->desc->linear_ranges[i].max_sel -
-                                    rdev->desc->linear_ranges[i].min_sel) + 1;
+       voltages = linear_range_values_in_range_array(r, range);
 
-       return val + voltages_in_range;
+       return val + voltages;
 }
 EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_pickable_regmap);
 
@@ -179,8 +178,11 @@ int regulator_set_voltage_sel_pickable_regmap(struct regulator_dev *rdev,
        unsigned int voltages_in_range = 0;
 
        for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
-               voltages_in_range = (rdev->desc->linear_ranges[i].max_sel -
-                                    rdev->desc->linear_ranges[i].min_sel) + 1;
+               const struct linear_range *r;
+
+               r = &rdev->desc->linear_ranges[i];
+               voltages_in_range = linear_range_values_in_range(r);
+
                if (sel < voltages_in_range)
                        break;
                sel -= voltages_in_range;
@@ -405,8 +407,10 @@ EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
 int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
                                       int min_uV, int max_uV)
 {
-       const struct regulator_linear_range *range;
+       const struct linear_range *range;
        int ret = -EINVAL;
+       unsigned int sel;
+       bool found;
        int voltage, i;
 
        if (!rdev->desc->n_linear_ranges) {
@@ -415,35 +419,19 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
        }
 
        for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
-               int linear_max_uV;
-
                range = &rdev->desc->linear_ranges[i];
-               linear_max_uV = range->min_uV +
-                       (range->max_sel - range->min_sel) * range->uV_step;
 
-               if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV))
+               ret = linear_range_get_selector_high(range, min_uV, &sel,
+                                                    &found);
+               if (ret)
                        continue;
-
-               if (min_uV <= range->min_uV)
-                       min_uV = range->min_uV;
-
-               /* range->uV_step == 0 means fixed voltage range */
-               if (range->uV_step == 0) {
-                       ret = 0;
-               } else {
-                       ret = DIV_ROUND_UP(min_uV - range->min_uV,
-                                          range->uV_step);
-                       if (ret < 0)
-                               return ret;
-               }
-
-               ret += range->min_sel;
+               ret = sel;
 
                /*
                 * Map back into a voltage to verify we're still in bounds.
                 * If we are not, then continue checking rest of the ranges.
                 */
-               voltage = rdev->desc->ops->list_voltage(rdev, ret);
+               voltage = rdev->desc->ops->list_voltage(rdev, sel);
                if (voltage >= min_uV && voltage <= max_uV)
                        break;
        }
@@ -468,7 +456,7 @@ EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range);
 int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
                                                int min_uV, int max_uV)
 {
-       const struct regulator_linear_range *range;
+       const struct linear_range *range;
        int ret = -EINVAL;
        int voltage, i;
        unsigned int selector = 0;
@@ -480,30 +468,25 @@ int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
 
        for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
                int linear_max_uV;
+               bool found;
+               unsigned int sel;
 
                range = &rdev->desc->linear_ranges[i];
-               linear_max_uV = range->min_uV +
-                       (range->max_sel - range->min_sel) * range->uV_step;
+               linear_max_uV = linear_range_get_max_value(range);
 
-               if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV)) {
-                       selector += (range->max_sel - range->min_sel + 1);
+               if (!(min_uV <= linear_max_uV && max_uV >= range->min)) {
+                       selector += linear_range_values_in_range(range);
                        continue;
                }
 
-               if (min_uV <= range->min_uV)
-                       min_uV = range->min_uV;
-
-               /* range->uV_step == 0 means fixed voltage range */
-               if (range->uV_step == 0) {
-                       ret = 0;
-               } else {
-                       ret = DIV_ROUND_UP(min_uV - range->min_uV,
-                                          range->uV_step);
-                       if (ret < 0)
-                               return ret;
+               ret = linear_range_get_selector_high(range, min_uV, &sel,
+                                                    &found);
+               if (ret) {
+                       selector += linear_range_values_in_range(range);
+                       continue;
                }
 
-               ret += selector;
+               ret = selector + sel;
 
                voltage = rdev->desc->ops->list_voltage(rdev, ret);
 
@@ -513,7 +496,7 @@ int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
                 * exit but retry until we have checked all ranges.
                 */
                if (voltage < min_uV || voltage > max_uV)
-                       selector += (range->max_sel - range->min_sel + 1);
+                       selector += linear_range_values_in_range(range);
                else
                        break;
        }
@@ -561,7 +544,7 @@ EXPORT_SYMBOL_GPL(regulator_list_voltage_linear);
 int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev,
                                                 unsigned int selector)
 {
-       const struct regulator_linear_range *range;
+       const struct linear_range *range;
        int i;
        unsigned int all_sels = 0;
 
@@ -571,18 +554,28 @@ int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev,
        }
 
        for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
-               unsigned int sels_in_range;
+               unsigned int sel_indexes;
 
                range = &rdev->desc->linear_ranges[i];
 
-               sels_in_range = range->max_sel - range->min_sel;
+               sel_indexes = linear_range_values_in_range(range) - 1;
 
-               if (all_sels + sels_in_range >= selector) {
+               if (all_sels + sel_indexes >= selector) {
                        selector -= all_sels;
-                       return range->min_uV + (range->uV_step * selector);
+                       /*
+                        * As we see here, pickable ranges work only as
+                        * long as the first selector for each pickable
+                        * range is 0, and the each subsequent range for
+                        * this 'pick' follow immediately at next unused
+                        * selector (Eg. there is no gaps between ranges).
+                        * I think this is fine but it probably should be
+                        * documented. OTOH, whole pickable range stuff
+                        * might benefit from some documentation
+                        */
+                       return range->min + (range->step * selector);
                }
 
-               all_sels += (sels_in_range + 1);
+               all_sels += (sel_indexes + 1);
        }
 
        return -EINVAL;
@@ -604,27 +597,18 @@ EXPORT_SYMBOL_GPL(regulator_list_voltage_pickable_linear_range);
 int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
                                             unsigned int selector)
 {
-       const struct regulator_linear_range *range;
-       int i;
-
-       if (!desc->n_linear_ranges) {
-               BUG_ON(!desc->n_linear_ranges);
-               return -EINVAL;
-       }
-
-       for (i = 0; i < desc->n_linear_ranges; i++) {
-               range = &desc->linear_ranges[i];
-
-               if (!(selector >= range->min_sel &&
-                     selector <= range->max_sel))
-                       continue;
+       unsigned int val;
+       int ret;
 
-               selector -= range->min_sel;
+       BUG_ON(!desc->n_linear_ranges);
 
-               return range->min_uV + (range->uV_step * selector);
-       }
+       ret = linear_range_get_value_array(desc->linear_ranges,
+                                          desc->n_linear_ranges, selector,
+                                          &val);
+       if (ret)
+               return ret;
 
-       return -EINVAL;
+       return val;
 }
 EXPORT_SYMBOL_GPL(regulator_desc_list_voltage_linear_range);
 
index 5ac3d7c29725a3a625293be7a584ac6c325b3c8c..66219d8dfc1a13b00be952bc0ef01d99e93e78d5 100644 (file)
@@ -87,7 +87,7 @@ static const unsigned int ldo_8_voltages[] = {
 };
 
 /* Ranges are sorted in ascending order. */
-static const struct regulator_linear_range ldo_audio_volt_range[] = {
+static const struct linear_range ldo_audio_volt_range[] = {
        REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000),
        REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000),
 };
@@ -195,7 +195,7 @@ static const struct regulator_ops hi6421_buck345_ops;
  * _id - LDO id name string
  * _match - of match name string
  * n_volt - number of votages available
- * volt_ranges - array of regulator_linear_range
+ * volt_ranges - array of linear_range
  * vstep - voltage increase in each linear step in uV
  * vreg - voltage select register
  * vmask - voltage select mask
index 9b05e03ba83056d7beedfb4199eed2febb2b1542..5ea3e41416849ce46db7647ef41b6dfc288d5dae 100644 (file)
@@ -36,7 +36,7 @@ static const struct regulator_ops lochnagar_micvdd_ops = {
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
 };
 
-static const struct regulator_linear_range lochnagar_micvdd_ranges[] = {
+static const struct linear_range lochnagar_micvdd_ranges[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0,    0xC, 50000),
        REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
 };
@@ -97,7 +97,7 @@ static const struct regulator_ops lochnagar_vddcore_ops = {
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
 };
 
-static const struct regulator_linear_range lochnagar_vddcore_ranges[] = {
+static const struct linear_range lochnagar_vddcore_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
 };
 
index b55de293ca7a48c81e3cb35e295fc3924fc33008..fe049b67e7d565ca0721f36e6165e0d5ce868b6f 100644 (file)
@@ -54,14 +54,14 @@ struct lp873x_regulator {
 
 static const struct lp873x_regulator regulators[];
 
-static const struct regulator_linear_range buck0_buck1_ranges[] = {
+static const struct linear_range buck0_buck1_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x13, 0),
        REGULATOR_LINEAR_RANGE(700000, 0x14, 0x17, 10000),
        REGULATOR_LINEAR_RANGE(735000, 0x18, 0x9d, 5000),
        REGULATOR_LINEAR_RANGE(1420000, 0x9e, 0xff, 20000),
 };
 
-static const struct regulator_linear_range ldo0_ldo1_ranges[] = {
+static const struct linear_range ldo0_ldo1_ranges[] = {
        REGULATOR_LINEAR_RANGE(800000, 0x0, 0x19, 100000),
 };
 
index 4ae12ac1f4c64452368034cd672b7f7eeea9172b..5d525dacf95990b90d8592ab1b536f414c960eaa 100644 (file)
@@ -46,7 +46,7 @@ struct lp87565_regulator {
 
 static const struct lp87565_regulator regulators[];
 
-static const struct regulator_linear_range buck0_1_2_3_ranges[] = {
+static const struct linear_range buck0_1_2_3_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0xA, 0x17, 10000),
        REGULATOR_LINEAR_RANGE(735000, 0x18, 0x9d, 5000),
        REGULATOR_LINEAR_RANGE(1420000, 0x9e, 0xff, 20000),
index 222502a296589ad32d96e897f7e256a978e19a7c..74b7b496b12d2cdbdcbc94a9df9729cfd9d09bc9 100644 (file)
@@ -92,7 +92,7 @@ struct lp8788_buck {
 };
 
 /* BUCK 1 ~ 4 voltage ranges */
-static const struct regulator_linear_range buck_volt_ranges[] = {
+static const struct linear_range buck_volt_ranges[] = {
        REGULATOR_LINEAR_RANGE(500000, 0, 0, 0),
        REGULATOR_LINEAR_RANGE(800000, 1, 25, 50000),
 };
index ac89a412f665ee11d7b6588b86a0bfa73b25c32b..ca08f94a368d0652d3334d15587008721117824b 100644 (file)
@@ -49,7 +49,7 @@ static const unsigned int max77651_sbb1_volt_range_sel[] = {
        0x0, 0x1, 0x2, 0x3
 };
 
-static const struct regulator_linear_range max77651_sbb1_volt_ranges[] = {
+static const struct linear_range max77651_sbb1_volt_ranges[] = {
        /* range index 0 */
        REGULATOR_LINEAR_RANGE(2400000, 0x00, 0x0f, 50000),
        /* range index 1 */
diff --git a/drivers/regulator/max77826-regulator.c b/drivers/regulator/max77826-regulator.c
new file mode 100644 (file)
index 0000000..502ab6a
--- /dev/null
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// max77826-regulator.c  - regulator driver for Maxim MAX77826
+//
+// Author: Iskren Chernev <iskren.chernev@gmail.com>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+enum max77826_registers {
+       MAX77826_REG_INT_SRC = 0x00,
+       MAX77826_REG_SYS_INT,
+       MAX77826_REG_INT1,
+       MAX77826_REG_INT2,
+       MAX77826_REG_BB_INT,
+       MAX77826_REG_INT_SRC_M,
+       MAX77826_REG_TOPSYS_INT_M,
+       MAX77826_REG_INT1_M,
+       MAX77826_REG_INT2_M,
+       MAX77826_REG_BB_INT_M,
+       MAX77826_REG_TOPSYS_STAT,
+       MAX77826_REG_STAT1,
+       MAX77826_REG_STAT2,
+       MAX77826_REG_BB_STAT,
+       /* 0x0E - 0x0F: Reserved */
+       MAX77826_REG_LDO_OPMD1 = 0x10,
+       MAX77826_REG_LDO_OPMD2,
+       MAX77826_REG_LDO_OPMD3,
+       MAX77826_REG_LDO_OPMD4,
+       MAX77826_REG_B_BB_OPMD,
+       /* 0x15 - 0x1F: Reserved */
+       MAX77826_REG_LDO1_CFG = 0x20,
+       MAX77826_REG_LDO2_CFG,
+       MAX77826_REG_LDO3_CFG,
+       MAX77826_REG_LDO4_CFG,
+       MAX77826_REG_LDO5_CFG,
+       MAX77826_REG_LDO6_CFG,
+       MAX77826_REG_LDO7_CFG,
+       MAX77826_REG_LDO8_CFG,
+       MAX77826_REG_LDO9_CFG,
+       MAX77826_REG_LDO10_CFG,
+       MAX77826_REG_LDO11_CFG,
+       MAX77826_REG_LDO12_CFG,
+       MAX77826_REG_LDO13_CFG,
+       MAX77826_REG_LDO14_CFG,
+       MAX77826_REG_LDO15_CFG,
+       /* 0x2F: Reserved */
+       MAX77826_REG_BUCK_CFG = 0x30,
+       MAX77826_REG_BUCK_VOUT,
+       MAX77826_REG_BB_CFG,
+       MAX77826_REG_BB_VOUT,
+       /* 0x34 - 0x3F: Reserved */
+       MAX77826_REG_BUCK_SS_FREQ = 0x40,
+       MAX77826_REG_UVLO_FALL,
+       /* 0x42 - 0xCE: Reserved */
+       MAX77826_REG_DEVICE_ID = 0xCF,
+};
+
+enum max77826_regulators {
+       MAX77826_LDO1 = 0,
+       MAX77826_LDO2,
+       MAX77826_LDO3,
+       MAX77826_LDO4,
+       MAX77826_LDO5,
+       MAX77826_LDO6,
+       MAX77826_LDO7,
+       MAX77826_LDO8,
+       MAX77826_LDO9,
+       MAX77826_LDO10,
+       MAX77826_LDO11,
+       MAX77826_LDO12,
+       MAX77826_LDO13,
+       MAX77826_LDO14,
+       MAX77826_LDO15,
+       MAX77826_BUCK,
+       MAX77826_BUCKBOOST,
+       MAX77826_MAX_REGULATORS,
+};
+
+#define MAX77826_MASK_LDO              0x7f
+#define MAX77826_MASK_BUCK             0xff
+#define MAX77826_MASK_BUCKBOOST                0x7f
+#define MAX77826_BUCK_RAMP_DELAY       12500
+
+/* values in mV */
+/* for LDO1-3 */
+#define MAX77826_NMOS_LDO_VOLT_MIN     600000
+#define MAX77826_NMOS_LDO_VOLT_MAX     2187500
+#define MAX77826_NMOS_LDO_VOLT_STEP    12500
+
+/* for LDO4-15 */
+#define MAX77826_PMOS_LDO_VOLT_MIN     800000
+#define MAX77826_PMOS_LDO_VOLT_MAX     3975000
+#define MAX77826_PMOS_LDO_VOLT_STEP    25000
+
+/* for BUCK */
+#define MAX77826_BUCK_VOLT_MIN         500000
+#define MAX77826_BUCK_VOLT_MAX         1800000
+#define MAX77826_BUCK_VOLT_STEP                6250
+
+/* for BUCKBOOST */
+#define MAX77826_BUCKBOOST_VOLT_MIN    2600000
+#define MAX77826_BUCKBOOST_VOLT_MAX    4187500
+#define MAX77826_BUCKBOOST_VOLT_STEP   12500
+#define MAX77826_VOLT_RANGE(_type)                                     \
+       ((MAX77826_ ## _type ## _VOLT_MAX -                             \
+         MAX77826_ ## _type ## _VOLT_MIN) /                            \
+        MAX77826_ ## _type ## _VOLT_STEP + 1)
+
+#define MAX77826_LDO(_id, _type)                                       \
+       [MAX77826_LDO ## _id] = {                                       \
+               .id = MAX77826_LDO ## _id,                              \
+               .name = "LDO"#_id,                                      \
+               .of_match = of_match_ptr("LDO"#_id),                    \
+               .regulators_node = "regulators",                        \
+               .ops = &max77826_most_ops,                              \
+               .min_uV = MAX77826_ ## _type ## _LDO_VOLT_MIN,          \
+               .uV_step = MAX77826_ ## _type ## _LDO_VOLT_STEP,        \
+               .n_voltages = MAX77826_VOLT_RANGE(_type ## _LDO),       \
+               .enable_reg = MAX77826_REG_LDO_OPMD1 + (_id - 1) / 4,   \
+               .enable_mask = BIT(((_id - 1) % 4) * 2 + 1),            \
+               .vsel_reg = MAX77826_REG_LDO1_CFG + (_id - 1),          \
+               .vsel_mask = MAX77826_MASK_LDO,                         \
+               .owner = THIS_MODULE,                                   \
+       }
+
+#define MAX77826_BUCK(_idx, _id, _ops)                                 \
+       [MAX77826_ ## _id] = {                                          \
+               .id = MAX77826_ ## _id,                                 \
+               .name = #_id,                                           \
+               .of_match = of_match_ptr(#_id),                         \
+               .regulators_node = "regulators",                        \
+               .ops = &_ops,                                           \
+               .min_uV =  MAX77826_ ## _id ## _VOLT_MIN,               \
+               .uV_step = MAX77826_ ## _id ## _VOLT_STEP,              \
+               .n_voltages = MAX77826_VOLT_RANGE(_id),                 \
+               .enable_reg = MAX77826_REG_B_BB_OPMD,                   \
+               .enable_mask = BIT(_idx * 2 + 1),                       \
+               .vsel_reg = MAX77826_REG_BUCK_VOUT + _idx * 2,          \
+               .vsel_mask = MAX77826_MASK_ ## _id,                     \
+               .owner = THIS_MODULE,                                   \
+       }
+
+
+
+struct max77826_regulator_info {
+       struct regmap *regmap;
+       struct regulator_desc *rdesc;
+};
+
+static const struct regmap_config max77826_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = MAX77826_REG_DEVICE_ID,
+};
+
+static int max77826_set_voltage_time_sel(struct regulator_dev *,
+                               unsigned int old_selector,
+                               unsigned int new_selector);
+
+static const struct regulator_ops max77826_most_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .map_voltage            = regulator_map_voltage_linear,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_ops max77826_buck_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .map_voltage            = regulator_map_voltage_linear,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .set_voltage_time_sel   = max77826_set_voltage_time_sel,
+};
+
+static struct regulator_desc max77826_regulators_desc[] = {
+       MAX77826_LDO(1, NMOS),
+       MAX77826_LDO(2, NMOS),
+       MAX77826_LDO(3, NMOS),
+       MAX77826_LDO(4, PMOS),
+       MAX77826_LDO(5, PMOS),
+       MAX77826_LDO(6, PMOS),
+       MAX77826_LDO(7, PMOS),
+       MAX77826_LDO(8, PMOS),
+       MAX77826_LDO(9, PMOS),
+       MAX77826_LDO(10, PMOS),
+       MAX77826_LDO(11, PMOS),
+       MAX77826_LDO(12, PMOS),
+       MAX77826_LDO(13, PMOS),
+       MAX77826_LDO(14, PMOS),
+       MAX77826_LDO(15, PMOS),
+       MAX77826_BUCK(0, BUCK, max77826_buck_ops),
+       MAX77826_BUCK(1, BUCKBOOST, max77826_most_ops),
+};
+
+static int max77826_set_voltage_time_sel(struct regulator_dev *rdev,
+                               unsigned int old_selector,
+                               unsigned int new_selector)
+{
+       if (new_selector > old_selector) {
+               return DIV_ROUND_UP(MAX77826_BUCK_VOLT_STEP *
+                               (new_selector - old_selector),
+                               MAX77826_BUCK_RAMP_DELAY);
+       }
+
+       return 0;
+}
+
+static int max77826_read_device_id(struct regmap *regmap, struct device *dev)
+{
+       unsigned int device_id;
+       int res;
+
+       res = regmap_read(regmap, MAX77826_REG_DEVICE_ID, &device_id);
+       if (!res)
+               dev_dbg(dev, "DEVICE_ID: 0x%x\n", device_id);
+
+       return res;
+}
+
+static int max77826_i2c_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct max77826_regulator_info *info;
+       struct regulator_config config = {};
+       struct regulator_dev *rdev;
+       struct regmap *regmap;
+       int i;
+
+       info = devm_kzalloc(dev, sizeof(struct max77826_regulator_info),
+                               GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->rdesc = max77826_regulators_desc;
+       regmap = devm_regmap_init_i2c(client, &max77826_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to allocate regmap!\n");
+               return PTR_ERR(regmap);
+       }
+
+       info->regmap = regmap;
+       i2c_set_clientdata(client, info);
+
+       config.dev = dev;
+       config.regmap = regmap;
+       config.driver_data = info;
+
+       for (i = 0; i < MAX77826_MAX_REGULATORS; i++) {
+               rdev = devm_regulator_register(dev,
+                                              &max77826_regulators_desc[i],
+                                              &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(dev, "Failed to register regulator!\n");
+                       return PTR_ERR(rdev);
+               }
+       }
+
+       return max77826_read_device_id(regmap, dev);
+}
+
+static const struct of_device_id max77826_of_match[] = {
+       { .compatible = "maxim,max77826" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max77826_of_match);
+
+static const struct i2c_device_id max77826_id[] = {
+       { "max77826-regulator" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, max77826_id);
+
+static struct i2c_driver max77826_regulator_driver = {
+       .driver = {
+               .name = "max77826",
+               .of_match_table = of_match_ptr(max77826_of_match),
+       },
+       .probe_new = max77826_i2c_probe,
+       .id_table = max77826_id,
+};
+module_i2c_driver(max77826_regulator_driver);
+
+MODULE_AUTHOR("Iskren Chernev <iskren.chernev@gmail.com>");
+MODULE_DESCRIPTION("MAX77826 PMIC regulator driver");
+MODULE_LICENSE("GPL");
index 60599c3bb845cc6d3a2b60ff3e0b0e44d745d027..340413bba0c5fe9ad72e1f1ea02c487c11c117d1 100644 (file)
@@ -33,6 +33,10 @@ struct max8998_data {
        unsigned int            buck2_idx;
 };
 
+static const unsigned int charger_current_table[] = {
+       90000, 380000, 475000, 550000, 570000, 600000, 700000, 800000,
+};
+
 static int max8998_get_enable_register(struct regulator_dev *rdev,
                                        int *reg, int *shift)
 {
@@ -63,6 +67,10 @@ static int max8998_get_enable_register(struct regulator_dev *rdev,
                *reg = MAX8998_REG_CHGR2;
                *shift = 7 - (ldo - MAX8998_ESAFEOUT1);
                break;
+       case MAX8998_CHARGER:
+               *reg = MAX8998_REG_CHGR2;
+               *shift = 0;
+               break;
        default:
                return -EINVAL;
        }
@@ -88,6 +96,11 @@ static int max8998_ldo_is_enabled(struct regulator_dev *rdev)
        return val & (1 << shift);
 }
 
+static int max8998_ldo_is_enabled_inverted(struct regulator_dev *rdev)
+{
+       return (!max8998_ldo_is_enabled(rdev));
+}
+
 static int max8998_ldo_enable(struct regulator_dev *rdev)
 {
        struct max8998_data *max8998 = rdev_get_drvdata(rdev);
@@ -358,6 +371,74 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev,
        return 0;
 }
 
+static int max8998_set_current_limit(struct regulator_dev *rdev,
+                                    int min_uA, int max_uA)
+{
+       struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max8998->iodev->i2c;
+       unsigned int n_currents = rdev->desc->n_current_limits;
+       int i, sel = -1;
+
+       if (n_currents == 0)
+               return -EINVAL;
+
+       if (rdev->desc->curr_table) {
+               const unsigned int *curr_table = rdev->desc->curr_table;
+               bool ascend = curr_table[n_currents - 1] > curr_table[0];
+
+               /* search for closest to maximum */
+               if (ascend) {
+                       for (i = n_currents - 1; i >= 0; i--) {
+                               if (min_uA <= curr_table[i] &&
+                                   curr_table[i] <= max_uA) {
+                                       sel = i;
+                                       break;
+                               }
+                       }
+               } else {
+                       for (i = 0; i < n_currents; i++) {
+                               if (min_uA <= curr_table[i] &&
+                                   curr_table[i] <= max_uA) {
+                                       sel = i;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if (sel < 0)
+               return -EINVAL;
+
+       sel <<= ffs(rdev->desc->csel_mask) - 1;
+
+       return max8998_update_reg(i2c, rdev->desc->csel_reg,
+                                 sel, rdev->desc->csel_mask);
+}
+
+int max8998_get_current_limit(struct regulator_dev *rdev)
+{
+       struct max8998_data *max8998 = rdev_get_drvdata(rdev);
+       struct i2c_client *i2c = max8998->iodev->i2c;
+       u8 val;
+       int ret;
+
+       ret = max8998_read_reg(i2c, rdev->desc->csel_reg, &val);
+       if (ret != 0)
+               return ret;
+
+       val &= rdev->desc->csel_mask;
+       val >>= ffs(rdev->desc->csel_mask) - 1;
+
+       if (rdev->desc->curr_table) {
+               if (val >= rdev->desc->n_current_limits)
+                       return -EINVAL;
+
+               return rdev->desc->curr_table[val];
+       }
+
+       return -EINVAL;
+}
+
 static const struct regulator_ops max8998_ldo_ops = {
        .list_voltage           = regulator_list_voltage_linear,
        .map_voltage            = regulator_map_voltage_linear,
@@ -379,6 +460,15 @@ static const struct regulator_ops max8998_buck_ops = {
        .set_voltage_time_sel   = max8998_set_voltage_buck_time_sel,
 };
 
+static const struct regulator_ops max8998_charger_ops = {
+       .set_current_limit      = max8998_set_current_limit,
+       .get_current_limit      = max8998_get_current_limit,
+       .is_enabled             = max8998_ldo_is_enabled_inverted,
+       /* Swapped as register is inverted */
+       .enable                 = max8998_ldo_disable,
+       .disable                = max8998_ldo_enable,
+};
+
 static const struct regulator_ops max8998_others_ops = {
        .is_enabled             = max8998_ldo_is_enabled,
        .enable                 = max8998_ldo_enable,
@@ -397,6 +487,19 @@ static const struct regulator_ops max8998_others_ops = {
                .owner = THIS_MODULE, \
        }
 
+#define MAX8998_CURRENT_REG(_name, _ops, _table, _reg, _mask) \
+       { \
+               .name = #_name, \
+               .id = MAX8998_##_name, \
+               .ops = _ops, \
+               .curr_table = _table, \
+               .n_current_limits = ARRAY_SIZE(_table), \
+               .csel_reg = _reg, \
+               .csel_mask = _mask, \
+               .type = REGULATOR_CURRENT, \
+               .owner = THIS_MODULE, \
+       }
+
 #define MAX8998_OTHERS_REG(_name, _id) \
        { \
                .name = #_name, \
@@ -432,6 +535,8 @@ static const struct regulator_desc regulators[] = {
        MAX8998_OTHERS_REG(ENVICHG, MAX8998_ENVICHG),
        MAX8998_OTHERS_REG(ESAFEOUT1, MAX8998_ESAFEOUT1),
        MAX8998_OTHERS_REG(ESAFEOUT2, MAX8998_ESAFEOUT2),
+       MAX8998_CURRENT_REG(CHARGER, &max8998_charger_ops,
+                           charger_current_table, MAX8998_REG_CHGR1, 0x7),
 };
 
 static int max8998_pmic_dt_parse_dvs_gpio(struct max8998_dev *iodev,
index e5a02711cb462c9975f8a55b666a7abf602895fc..6d0ad74935b35cefba2f4cf334c343d2d87f5b55 100644 (file)
@@ -391,11 +391,11 @@ static const struct of_device_id mcp16502_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, mcp16502_ids);
 
-static const struct regulator_linear_range b1l12_ranges[] = {
+static const struct linear_range b1l12_ranges[] = {
        REGULATOR_LINEAR_RANGE(1200000, VDD_LOW_SEL, VDD_HIGH_SEL, 50000),
 };
 
-static const struct regulator_linear_range b234_ranges[] = {
+static const struct linear_range b234_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, VDD_LOW_SEL, VDD_HIGH_SEL, 25000),
 };
 
index 6ed987648188702045a59eaa65f67ddc5791c5b2..f2300714d5a96ee9db591ab9941ba2203f752b22 100644 (file)
@@ -73,7 +73,7 @@ static int mp8859_get_voltage_sel(struct regulator_dev *rdev)
        return val;
 }
 
-static const struct regulator_linear_range mp8859_dcdc_ranges[] = {
+static const struct linear_range mp8859_dcdc_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, VOL_MIN_IDX, VOL_MAX_IDX, 10000),
 };
 
index 893ea190788a70b29f726fdcf08551be76a5241e..ff9016170db37482efe22115de0ef9fe64d76fe7 100644 (file)
@@ -102,15 +102,15 @@ struct mt6323_regulator_info {
        .modeset_mask = _modeset_mask,                                  \
 }
 
-static const struct regulator_linear_range buck_volt_range1[] = {
+static const struct linear_range buck_volt_range1[] = {
        REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range2[] = {
+static const struct linear_range buck_volt_range2[] = {
        REGULATOR_LINEAR_RANGE(1400000, 0, 0x7f, 12500),
 };
 
-static const struct regulator_linear_range buck_volt_range3[] = {
+static const struct linear_range buck_volt_range3[] = {
        REGULATOR_LINEAR_RANGE(500000, 0, 0x3f, 50000),
 };
 
index ba42682e06f36e956b955b85d866b6cc426bbf2c..13cb6ac9a8929463c80959982b8bb43404023ccb 100644 (file)
@@ -137,19 +137,19 @@ struct mt6358_regulator_info {
        .qi = BIT(15),                                                  \
 }
 
-static const struct regulator_linear_range buck_volt_range1[] = {
+static const struct linear_range buck_volt_range1[] = {
        REGULATOR_LINEAR_RANGE(500000, 0, 0x7f, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range2[] = {
+static const struct linear_range buck_volt_range2[] = {
        REGULATOR_LINEAR_RANGE(500000, 0, 0x7f, 12500),
 };
 
-static const struct regulator_linear_range buck_volt_range3[] = {
+static const struct linear_range buck_volt_range3[] = {
        REGULATOR_LINEAR_RANGE(500000, 0, 0x3f, 50000),
 };
 
-static const struct regulator_linear_range buck_volt_range4[] = {
+static const struct linear_range buck_volt_range4[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0, 0x7f, 12500),
 };
 
index b6aed090b5e0c0137f2fa97846591005ef9f1a1b..9efd8710a6f32a73e843806a97313253e7a4ff7d 100644 (file)
@@ -152,15 +152,15 @@ struct mt6380_regulator_info {
        .modeset_mask = _modeset_mask,                                  \
 }
 
-static const struct regulator_linear_range buck_volt_range1[] = {
+static const struct linear_range buck_volt_range1[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 0xfe, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range2[] = {
+static const struct linear_range buck_volt_range2[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 0xfe, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range3[] = {
+static const struct linear_range buck_volt_range3[] = {
        REGULATOR_LINEAR_RANGE(1200000, 0, 0x3c, 25000),
 };
 
index fd9ed864a0c148aee2e86c156e22d0587d59250a..269c2a6028e85302eadaa7462967b0eae8bb1449 100644 (file)
@@ -102,15 +102,15 @@ struct mt6397_regulator_info {
        .qi = BIT(15),                                                  \
 }
 
-static const struct regulator_linear_range buck_volt_range1[] = {
+static const struct linear_range buck_volt_range1[] = {
        REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range2[] = {
+static const struct linear_range buck_volt_range2[] = {
        REGULATOR_LINEAR_RANGE(800000, 0, 0x7f, 6250),
 };
 
-static const struct regulator_linear_range buck_volt_range3[] = {
+static const struct linear_range buck_volt_range3[] = {
        REGULATOR_LINEAR_RANGE(1500000, 0, 0x1f, 20000),
 };
 
index 31325912d311d05e2e34bff2af93ebd310e605db..337dd614695e4d14f0125841d0c7e7e02ed01a7e 100644 (file)
 #include <linux/of_platform.h>
 #include <linux/regulator/of_regulator.h>
 
-static const struct regulator_linear_range smps_low_ranges[] = {
+static const struct linear_range smps_low_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(500000, 0x1, 0x6, 0),
        REGULATOR_LINEAR_RANGE(510000, 0x7, 0x79, 10000),
        REGULATOR_LINEAR_RANGE(1650000, 0x7A, 0x7f, 0),
 };
 
-static const struct regulator_linear_range smps_high_ranges[] = {
+static const struct linear_range smps_high_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x6, 0),
        REGULATOR_LINEAR_RANGE(1020000, 0x7, 0x79, 20000),
index c86ad40015ce887888a0b055f6f87a5d9ef779b2..79bdc129cb5040d2f5184deeb4547afa35b62fa8 100644 (file)
@@ -86,7 +86,7 @@ enum rpmh_regulator_type {
 struct rpmh_vreg_hw_data {
        enum rpmh_regulator_type                regulator_type;
        const struct regulator_ops              *ops;
-       const struct regulator_linear_range     voltage_range;
+       const struct linear_range       voltage_range;
        int                                     n_voltages;
        int                                     hpm_min_load_uA;
        const int                               *pmic_mode_map;
@@ -832,11 +832,11 @@ static const struct rpmh_vreg_init_data pm8150_vreg_data[] = {
        RPMH_VREG("ldo10",  "ldo%s10", &pmic5_pldo,      "vdd-l2-l10"),
        RPMH_VREG("ldo11",  "ldo%s11", &pmic5_nldo,      "vdd-l1-l8-l11"),
        RPMH_VREG("ldo12",  "ldo%s12", &pmic5_pldo_lv,   "vdd-l7-l12-l14-l15"),
-       RPMH_VREG("ldo13",  "ldo%s13", &pmic5_pldo,      "vdd-l13-l6-l17"),
+       RPMH_VREG("ldo13",  "ldo%s13", &pmic5_pldo,      "vdd-l13-l16-l17"),
        RPMH_VREG("ldo14",  "ldo%s14", &pmic5_pldo_lv,   "vdd-l7-l12-l14-l15"),
        RPMH_VREG("ldo15",  "ldo%s15", &pmic5_pldo_lv,   "vdd-l7-l12-l14-l15"),
-       RPMH_VREG("ldo16",  "ldo%s16", &pmic5_pldo,      "vdd-l13-l6-l17"),
-       RPMH_VREG("ldo17",  "ldo%s17", &pmic5_pldo,      "vdd-l13-l6-l17"),
+       RPMH_VREG("ldo16",  "ldo%s16", &pmic5_pldo,      "vdd-l13-l16-l17"),
+       RPMH_VREG("ldo17",  "ldo%s17", &pmic5_pldo,      "vdd-l13-l16-l17"),
        RPMH_VREG("ldo18",  "ldo%s18", &pmic5_nldo,      "vdd-l3-l4-l5-l18"),
        {},
 };
@@ -857,7 +857,7 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = {
        RPMH_VREG("ldo5",   "ldo%s5",  &pmic5_pldo,      "vdd-l4-l5-l6"),
        RPMH_VREG("ldo6",   "ldo%s6",  &pmic5_pldo,      "vdd-l4-l5-l6"),
        RPMH_VREG("ldo7",   "ldo%s7",  &pmic5_pldo,      "vdd-l7-l11"),
-       RPMH_VREG("ldo8",   "ldo%s8",  &pmic5_pldo_lv,   "vdd-l1-l8-l11"),
+       RPMH_VREG("ldo8",   "ldo%s8",  &pmic5_pldo_lv,   "vdd-l1-l8"),
        RPMH_VREG("ldo9",   "ldo%s9",  &pmic5_pldo,      "vdd-l9-l10"),
        RPMH_VREG("ldo10",  "ldo%s10", &pmic5_pldo,      "vdd-l9-l10"),
        RPMH_VREG("ldo11",  "ldo%s11", &pmic5_pldo,      "vdd-l7-l11"),
index 7fc97f23fcf43770ae47d204b3621dabda4b6eb4..0066f850f15d60d9833acaa018807aedd5c406ec 100644 (file)
@@ -148,41 +148,41 @@ static const struct rpm_reg_parts rpm8960_ncp_parts = {
 /*
  * Physically available PMIC regulator voltage ranges
  */
-static const struct regulator_linear_range pldo_ranges[] = {
+static const struct linear_range pldo_ranges[] = {
        REGULATOR_LINEAR_RANGE( 750000,   0,  59, 12500),
        REGULATOR_LINEAR_RANGE(1500000,  60, 123, 25000),
        REGULATOR_LINEAR_RANGE(3100000, 124, 160, 50000),
 };
 
-static const struct regulator_linear_range nldo_ranges[] = {
+static const struct linear_range nldo_ranges[] = {
        REGULATOR_LINEAR_RANGE( 750000,   0,  63, 12500),
 };
 
-static const struct regulator_linear_range nldo1200_ranges[] = {
+static const struct linear_range nldo1200_ranges[] = {
        REGULATOR_LINEAR_RANGE( 375000,   0,  59,  6250),
        REGULATOR_LINEAR_RANGE( 750000,  60, 123, 12500),
 };
 
-static const struct regulator_linear_range smps_ranges[] = {
+static const struct linear_range smps_ranges[] = {
        REGULATOR_LINEAR_RANGE( 375000,   0,  29, 12500),
        REGULATOR_LINEAR_RANGE( 750000,  30,  89, 12500),
        REGULATOR_LINEAR_RANGE(1500000,  90, 153, 25000),
 };
 
-static const struct regulator_linear_range ftsmps_ranges[] = {
+static const struct linear_range ftsmps_ranges[] = {
        REGULATOR_LINEAR_RANGE( 350000,   0,   6, 50000),
        REGULATOR_LINEAR_RANGE( 700000,   7,  63, 12500),
        REGULATOR_LINEAR_RANGE(1500000,  64, 100, 50000),
 };
 
-static const struct regulator_linear_range smb208_ranges[] = {
+static const struct linear_range smb208_ranges[] = {
        REGULATOR_LINEAR_RANGE( 375000,   0,  29, 12500),
        REGULATOR_LINEAR_RANGE( 750000,  30,  89, 12500),
        REGULATOR_LINEAR_RANGE(1500000,  90, 153, 25000),
        REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000),
 };
 
-static const struct regulator_linear_range ncp_ranges[] = {
+static const struct linear_range ncp_ranges[] = {
        REGULATOR_LINEAR_RANGE(1500000,   0,  31, 50000),
 };
 
@@ -604,16 +604,6 @@ static const struct qcom_rpm_reg pm8921_smps = {
        .supports_force_mode_bypass = false,
 };
 
-static const struct qcom_rpm_reg pm8921_ftsmps = {
-       .desc.linear_ranges = ftsmps_ranges,
-       .desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
-       .desc.n_voltages = 101,
-       .desc.ops = &uV_ops,
-       .parts = &rpm8960_smps_parts,
-       .supports_force_mode_auto = true,
-       .supports_force_mode_bypass = false,
-};
-
 static const struct qcom_rpm_reg pm8921_ncp = {
        .desc.linear_ranges = ncp_ranges,
        .desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
index fdde4195cefba116640c9d4793076ebd57e010d5..53a64d856926f7e4bf4aecd2dd495fa37fd8df8a 100644 (file)
@@ -199,7 +199,7 @@ static const struct regulator_ops rpm_bob_ops = {
 };
 
 static const struct regulator_desc pma8084_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(375000,  0,  95, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
        },
@@ -209,7 +209,7 @@ static const struct regulator_desc pma8084_hfsmps = {
 };
 
 static const struct regulator_desc pma8084_ftsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(350000,  0, 184, 5000),
                REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
        },
@@ -219,7 +219,7 @@ static const struct regulator_desc pma8084_ftsmps = {
 };
 
 static const struct regulator_desc pma8084_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE( 750000,  0,  63, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
                REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
@@ -230,7 +230,7 @@ static const struct regulator_desc pma8084_pldo = {
 };
 
 static const struct regulator_desc pma8084_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
        },
        .n_linear_ranges = 1,
@@ -243,7 +243,7 @@ static const struct regulator_desc pma8084_switch = {
 };
 
 static const struct regulator_desc pm8x41_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE( 375000,  0,  95, 12500),
                REGULATOR_LINEAR_RANGE(1575000, 96, 158, 25000),
        },
@@ -253,7 +253,7 @@ static const struct regulator_desc pm8x41_hfsmps = {
 };
 
 static const struct regulator_desc pm8841_ftsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(350000,  0, 184, 5000),
                REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
        },
@@ -263,7 +263,7 @@ static const struct regulator_desc pm8841_ftsmps = {
 };
 
 static const struct regulator_desc pm8941_boost = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000),
        },
        .n_linear_ranges = 1,
@@ -272,7 +272,7 @@ static const struct regulator_desc pm8941_boost = {
 };
 
 static const struct regulator_desc pm8941_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE( 750000,  0,  63, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
                REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
@@ -283,7 +283,7 @@ static const struct regulator_desc pm8941_pldo = {
 };
 
 static const struct regulator_desc pm8941_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
        },
        .n_linear_ranges = 1,
@@ -302,7 +302,7 @@ static const struct regulator_desc pm8941_switch = {
 };
 
 static const struct regulator_desc pm8916_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500),
        },
        .n_linear_ranges = 1,
@@ -311,7 +311,7 @@ static const struct regulator_desc pm8916_pldo = {
 };
 
 static const struct regulator_desc pm8916_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(375000, 0, 93, 12500),
        },
        .n_linear_ranges = 1,
@@ -320,7 +320,7 @@ static const struct regulator_desc pm8916_nldo = {
 };
 
 static const struct regulator_desc pm8916_buck_lvo_smps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
                REGULATOR_LINEAR_RANGE(750000, 96, 127, 25000),
        },
@@ -330,7 +330,7 @@ static const struct regulator_desc pm8916_buck_lvo_smps = {
 };
 
 static const struct regulator_desc pm8916_buck_hvo_smps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1550000, 0, 31, 25000),
        },
        .n_linear_ranges = 1,
@@ -339,7 +339,7 @@ static const struct regulator_desc pm8916_buck_hvo_smps = {
 };
 
 static const struct regulator_desc pm8950_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 96, 127, 25000),
        },
@@ -349,7 +349,7 @@ static const struct regulator_desc pm8950_hfsmps = {
 };
 
 static const struct regulator_desc pm8950_ftsmps2p5 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(80000, 0, 255, 5000),
                REGULATOR_LINEAR_RANGE(160000, 256, 460, 10000),
        },
@@ -359,7 +359,7 @@ static const struct regulator_desc pm8950_ftsmps2p5 = {
 };
 
 static const struct regulator_desc pm8950_ult_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(375000, 0, 202, 12500),
        },
        .n_linear_ranges = 1,
@@ -368,7 +368,7 @@ static const struct regulator_desc pm8950_ult_nldo = {
 };
 
 static const struct regulator_desc pm8950_ult_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1750000, 0, 127, 12500),
        },
        .n_linear_ranges = 1,
@@ -377,7 +377,7 @@ static const struct regulator_desc pm8950_ult_pldo = {
 };
 
 static const struct regulator_desc pm8950_pldo_lv = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1500000, 0, 16, 25000),
        },
        .n_linear_ranges = 1,
@@ -386,7 +386,7 @@ static const struct regulator_desc pm8950_pldo_lv = {
 };
 
 static const struct regulator_desc pm8950_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(975000, 0, 164, 12500),
        },
        .n_linear_ranges = 1,
@@ -396,7 +396,7 @@ static const struct regulator_desc pm8950_pldo = {
 
 
 static const struct regulator_desc pm8994_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE( 375000,  0,  95, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
        },
@@ -406,7 +406,7 @@ static const struct regulator_desc pm8994_hfsmps = {
 };
 
 static const struct regulator_desc pm8994_ftsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(350000,  0, 199, 5000),
                REGULATOR_LINEAR_RANGE(700000, 200, 349, 10000),
        },
@@ -416,7 +416,7 @@ static const struct regulator_desc pm8994_ftsmps = {
 };
 
 static const struct regulator_desc pm8994_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
        },
        .n_linear_ranges = 1,
@@ -425,7 +425,7 @@ static const struct regulator_desc pm8994_nldo = {
 };
 
 static const struct regulator_desc pm8994_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE( 750000,  0,  63, 12500),
                REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
                REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
@@ -446,7 +446,7 @@ static const struct regulator_desc pm8994_lnldo = {
 };
 
 static const struct regulator_desc pmi8994_ftsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(350000,  0, 199, 5000),
                REGULATOR_LINEAR_RANGE(700000, 200, 349, 10000),
        },
@@ -456,7 +456,7 @@ static const struct regulator_desc pmi8994_ftsmps = {
 };
 
 static const struct regulator_desc pmi8994_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(350000,  0,  80, 12500),
                REGULATOR_LINEAR_RANGE(700000, 81, 141, 25000),
        },
@@ -466,7 +466,7 @@ static const struct regulator_desc pmi8994_hfsmps = {
 };
 
 static const struct regulator_desc pmi8994_bby = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(3000000, 0, 44, 50000),
        },
        .n_linear_ranges = 1,
@@ -475,7 +475,7 @@ static const struct regulator_desc pmi8994_bby = {
 };
 
 static const struct regulator_desc pmi8994_boost = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000),
        },
        .n_linear_ranges = 1,
@@ -484,7 +484,7 @@ static const struct regulator_desc pmi8994_boost = {
 };
 
 static const struct regulator_desc pm8998_ftsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
        },
        .n_linear_ranges = 1,
@@ -493,7 +493,7 @@ static const struct regulator_desc pm8998_ftsmps = {
 };
 
 static const struct regulator_desc pm8998_hfsmps = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
        },
        .n_linear_ranges = 1,
@@ -502,7 +502,7 @@ static const struct regulator_desc pm8998_hfsmps = {
 };
 
 static const struct regulator_desc pm8998_nldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
        },
        .n_linear_ranges = 1,
@@ -511,7 +511,7 @@ static const struct regulator_desc pm8998_nldo = {
 };
 
 static const struct regulator_desc pm8998_pldo = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
        },
        .n_linear_ranges = 1,
@@ -520,7 +520,7 @@ static const struct regulator_desc pm8998_pldo = {
 };
 
 static const struct regulator_desc pm8998_pldo_lv = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
        },
        .n_linear_ranges = 1,
@@ -533,7 +533,7 @@ static const struct regulator_desc pm8998_switch = {
 };
 
 static const struct regulator_desc pmi8998_bob = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
        },
        .n_linear_ranges = 1,
@@ -542,7 +542,7 @@ static const struct regulator_desc pmi8998_bob = {
 };
 
 static const struct regulator_desc pms405_hfsmps3 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
        },
        .n_linear_ranges = 1,
@@ -551,7 +551,7 @@ static const struct regulator_desc pms405_hfsmps3 = {
 };
 
 static const struct regulator_desc pms405_nldo300 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
        },
        .n_linear_ranges = 1,
@@ -560,7 +560,7 @@ static const struct regulator_desc pms405_nldo300 = {
 };
 
 static const struct regulator_desc pms405_nldo1200 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
        },
        .n_linear_ranges = 1,
@@ -569,7 +569,7 @@ static const struct regulator_desc pms405_nldo1200 = {
 };
 
 static const struct regulator_desc pms405_pldo50 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1664000, 0, 128, 16000),
        },
        .n_linear_ranges = 1,
@@ -578,7 +578,7 @@ static const struct regulator_desc pms405_pldo50 = {
 };
 
 static const struct regulator_desc pms405_pldo150 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1664000, 0, 128, 16000),
        },
        .n_linear_ranges = 1,
@@ -587,7 +587,7 @@ static const struct regulator_desc pms405_pldo150 = {
 };
 
 static const struct regulator_desc pms405_pldo600 = {
-       .linear_ranges = (struct regulator_linear_range[]) {
+       .linear_ranges = (struct linear_range[]) {
                REGULATOR_LINEAR_RANGE(1256000, 0, 98, 8000),
        },
        .n_linear_ranges = 1,
index 31f79fda3238b5a6b476a0d24bd83179aa946ffe..e926c1a85846058f518f6c9f61364fabdecf9502 100644 (file)
@@ -165,14 +165,14 @@ static const int rk808_buck_config_regs[] = {
        RK808_BUCK4_CONFIG_REG,
 };
 
-static const struct regulator_linear_range rk808_ldo3_voltage_ranges[] = {
+static const struct linear_range rk808_ldo3_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(800000, 0, 13, 100000),
        REGULATOR_LINEAR_RANGE(2500000, 15, 15, 0),
 };
 
 #define RK809_BUCK5_SEL_CNT            (8)
 
-static const struct regulator_linear_range rk809_buck5_voltage_ranges[] = {
+static const struct linear_range rk809_buck5_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(1500000, 0, 0, 0),
        REGULATOR_LINEAR_RANGE(1800000, 1, 3, 200000),
        REGULATOR_LINEAR_RANGE(2800000, 4, 5, 200000),
@@ -201,14 +201,14 @@ static const struct regulator_linear_range rk809_buck5_voltage_ranges[] = {
 #define RK817_BUCK1_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK1_SEL1 + 1)
 #define RK817_BUCK3_SEL_CNT (RK817_BUCK1_SEL0 + RK817_BUCK3_SEL1 + 1)
 
-static const struct regulator_linear_range rk817_buck1_voltage_ranges[] = {
+static const struct linear_range rk817_buck1_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0,
                               RK817_BUCK1_SEL0, RK817_BUCK1_STP0),
        REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1,
                               RK817_BUCK1_SEL_CNT, RK817_BUCK1_STP1),
 };
 
-static const struct regulator_linear_range rk817_buck3_voltage_ranges[] = {
+static const struct linear_range rk817_buck3_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN0, 0,
                               RK817_BUCK1_SEL0, RK817_BUCK1_STP0),
        REGULATOR_LINEAR_RANGE(RK817_BUCK1_MIN1, RK817_BUCK1_SEL0 + 1,
@@ -665,7 +665,7 @@ static const struct regulator_ops rk808_switch_ops = {
        .set_suspend_disable    = rk808_set_suspend_disable,
 };
 
-static const struct regulator_linear_range rk805_buck_1_2_voltage_ranges[] = {
+static const struct linear_range rk805_buck_1_2_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(712500, 0, 59, 12500),
        REGULATOR_LINEAR_RANGE(1800000, 60, 62, 200000),
        REGULATOR_LINEAR_RANGE(2300000, 63, 63, 0),
index 23d288278957563d43f3491f557ee890c58a25e6..33cf84bce05afd8de6b11c911e294a0419916a57 100644 (file)
@@ -749,37 +749,37 @@ static const struct regulator_ops s2mps15_reg_buck_ops = {
 }
 
 /* voltage range for s2mps15 LDO 3, 5, 15, 16, 18, 20, 23 and 27 */
-static const struct regulator_linear_range s2mps15_ldo_voltage_ranges1[] = {
+static const struct linear_range s2mps15_ldo_voltage_ranges1[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0xc, 0x38, 25000),
 };
 
 /* voltage range for s2mps15 LDO 2, 6, 14, 17, 19, 21, 24 and 25 */
-static const struct regulator_linear_range s2mps15_ldo_voltage_ranges2[] = {
+static const struct linear_range s2mps15_ldo_voltage_ranges2[] = {
        REGULATOR_LINEAR_RANGE(1800000, 0x0, 0x3f, 25000),
 };
 
 /* voltage range for s2mps15 LDO 4, 11, 12, 13, 22 and 26 */
-static const struct regulator_linear_range s2mps15_ldo_voltage_ranges3[] = {
+static const struct linear_range s2mps15_ldo_voltage_ranges3[] = {
        REGULATOR_LINEAR_RANGE(700000, 0x0, 0x34, 12500),
 };
 
 /* voltage range for s2mps15 LDO 7, 8, 9 and 10 */
-static const struct regulator_linear_range s2mps15_ldo_voltage_ranges4[] = {
+static const struct linear_range s2mps15_ldo_voltage_ranges4[] = {
        REGULATOR_LINEAR_RANGE(700000, 0x10, 0x20, 25000),
 };
 
 /* voltage range for s2mps15 LDO 1 */
-static const struct regulator_linear_range s2mps15_ldo_voltage_ranges5[] = {
+static const struct linear_range s2mps15_ldo_voltage_ranges5[] = {
        REGULATOR_LINEAR_RANGE(500000, 0x0, 0x20, 12500),
 };
 
 /* voltage range for s2mps15 BUCK 1, 2, 3, 4, 5, 6 and 7 */
-static const struct regulator_linear_range s2mps15_buck_voltage_ranges1[] = {
+static const struct linear_range s2mps15_buck_voltage_ranges1[] = {
        REGULATOR_LINEAR_RANGE(500000, 0x20, 0xc0, 6250),
 };
 
 /* voltage range for s2mps15 BUCK 8, 9 and 10 */
-static const struct regulator_linear_range s2mps15_buck_voltage_ranges2[] = {
+static const struct linear_range s2mps15_buck_voltage_ranges2[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0x20, 0x78, 12500),
 };
 
index 177dede82a619cad98b7cd12c321b8c89fc0eba2..37658affe072de82affce1d68ee7a6e0ca6e5063 100644 (file)
@@ -32,7 +32,7 @@ static const struct regulator_ops sky81452_reg_ops = {
        .is_enabled = regulator_is_enabled_regmap,
 };
 
-static const struct regulator_linear_range sky81452_reg_ranges[] = {
+static const struct linear_range sky81452_reg_ranges[] = {
        REGULATOR_LINEAR_RANGE(4500000, 0, 14, 250000),
        REGULATOR_LINEAR_RANGE(9000000, 15, 31, 1000000),
 };
index f3d7d007ecbb43f110cbbaa76401adf1dcbf76a8..adc9973d1b2f7b7340317f34f21c384b81a214c3 100644 (file)
@@ -57,13 +57,13 @@ enum {
 /* Ramp delay worst case is (2250uV/uS) */
 #define PMIC_RAMP_DELAY 2200
 
-static const struct regulator_linear_range buck1_ranges[] = {
+static const struct linear_range buck1_ranges[] = {
        REGULATOR_LINEAR_RANGE(725000, 0, 4, 0),
        REGULATOR_LINEAR_RANGE(725000, 5, 36, 25000),
        REGULATOR_LINEAR_RANGE(1500000, 37, 63, 0),
 };
 
-static const struct regulator_linear_range buck2_ranges[] = {
+static const struct linear_range buck2_ranges[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0, 17, 0),
        REGULATOR_LINEAR_RANGE(1050000, 18, 19, 0),
        REGULATOR_LINEAR_RANGE(1100000, 20, 21, 0),
@@ -77,7 +77,7 @@ static const struct regulator_linear_range buck2_ranges[] = {
        REGULATOR_LINEAR_RANGE(1500000, 36, 63, 0),
 };
 
-static const struct regulator_linear_range buck3_ranges[] = {
+static const struct linear_range buck3_ranges[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0, 19, 0),
        REGULATOR_LINEAR_RANGE(1100000, 20, 23, 0),
        REGULATOR_LINEAR_RANGE(1200000, 24, 27, 0),
@@ -87,7 +87,7 @@ static const struct regulator_linear_range buck3_ranges[] = {
        REGULATOR_LINEAR_RANGE(3400000, 56, 63, 0),
 };
 
-static const struct regulator_linear_range buck4_ranges[] = {
+static const struct linear_range buck4_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 27, 25000),
        REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0),
        REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0),
@@ -97,19 +97,19 @@ static const struct regulator_linear_range buck4_ranges[] = {
        REGULATOR_LINEAR_RANGE(3900000, 61, 63, 0),
 };
 
-static const struct regulator_linear_range ldo1_ranges[] = {
+static const struct linear_range ldo1_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
        REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
        REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
 };
 
-static const struct regulator_linear_range ldo2_ranges[] = {
+static const struct linear_range ldo2_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
        REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
        REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
 };
 
-static const struct regulator_linear_range ldo3_ranges[] = {
+static const struct linear_range ldo3_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
        REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000),
        REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0),
@@ -117,13 +117,13 @@ static const struct regulator_linear_range ldo3_ranges[] = {
        REGULATOR_LINEAR_RANGE(500000, 31, 31, 0),
 };
 
-static const struct regulator_linear_range ldo5_ranges[] = {
+static const struct linear_range ldo5_ranges[] = {
        REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0),
        REGULATOR_LINEAR_RANGE(1700000, 8, 30, 100000),
        REGULATOR_LINEAR_RANGE(3900000, 31, 31, 0),
 };
 
-static const struct regulator_linear_range ldo6_ranges[] = {
+static const struct linear_range ldo6_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 24, 100000),
        REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0),
 };
index 5a5e9b5bf4bea7c6935cca2423dce30475c4177c..9910e949373c3d3d8c6dd0520968c1a1712afc4d 100644 (file)
@@ -71,23 +71,23 @@ struct tps65086_regulator {
        unsigned int decay_mask;
 };
 
-static const struct regulator_linear_range tps65086_10mv_ranges[] = {
+static const struct linear_range tps65086_10mv_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000),
 };
 
-static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = {
+static const struct linear_range tps65086_buck126_25mv_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0),
        REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000),
 };
 
-static const struct regulator_linear_range tps65086_buck345_25mv_ranges[] = {
+static const struct linear_range tps65086_buck345_25mv_ranges[] = {
        REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000),
 };
 
-static const struct regulator_linear_range tps65086_ldoa1_ranges[] = {
+static const struct linear_range tps65086_ldoa1_ranges[] = {
        REGULATOR_LINEAR_RANGE(1350000, 0x0, 0x0, 0),
        REGULATOR_LINEAR_RANGE(1500000, 0x1, 0x7, 100000),
        REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xB, 100000),
@@ -95,7 +95,7 @@ static const struct regulator_linear_range tps65086_ldoa1_ranges[] = {
        REGULATOR_LINEAR_RANGE(3300000, 0xE, 0xE, 0),
 };
 
-static const struct regulator_linear_range tps65086_ldoa23_ranges[] = {
+static const struct linear_range tps65086_ldoa23_ranges[] = {
        REGULATOR_LINEAR_RANGE(700000, 0x0, 0xD, 50000),
        REGULATOR_LINEAR_RANGE(1400000, 0xE, 0xF, 100000),
 };
index 67ba78da77ecc95b67056f700811f246cab3a908..d27dbbafcf721a2c47149503a6e2d882c1ee8561 100644 (file)
@@ -56,14 +56,14 @@ static const unsigned int LDO1_VSEL_table[] = {
        2800000, 3000000, 3100000, 3300000,
 };
 
-static const struct regulator_linear_range tps65217_uv1_ranges[] = {
+static const struct linear_range tps65217_uv1_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 24, 25000),
        REGULATOR_LINEAR_RANGE(1550000, 25, 52, 50000),
        REGULATOR_LINEAR_RANGE(3000000, 53, 55, 100000),
        REGULATOR_LINEAR_RANGE(3300000, 56, 63, 0),
 };
 
-static const struct regulator_linear_range tps65217_uv2_ranges[] = {
+static const struct linear_range tps65217_uv2_ranges[] = {
        REGULATOR_LINEAR_RANGE(1500000, 0, 8, 50000),
        REGULATOR_LINEAR_RANGE(2000000, 9, 13, 100000),
        REGULATOR_LINEAR_RANGE(2450000, 14, 31, 50000),
index b72035610013e7b45646b7cd0a0856a62d5cec8e..05d13f807918217f2ca86884dffde44c3357ae04 100644 (file)
                .bypass_mask    = _sm,                          \
        }                                                       \
 
-static const struct regulator_linear_range dcdc1_dcdc2_ranges[] = {
+static const struct linear_range dcdc1_dcdc2_ranges[] = {
        REGULATOR_LINEAR_RANGE(850000, 0x0, 0x32, 10000),
        REGULATOR_LINEAR_RANGE(1375000, 0x33, 0x3f, 25000),
 };
 
-static const struct regulator_linear_range ldo1_dcdc3_ranges[] = {
+static const struct linear_range ldo1_dcdc3_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0x0, 0x1a, 25000),
        REGULATOR_LINEAR_RANGE(1600000, 0x1b, 0x3f, 50000),
 };
 
-static const struct regulator_linear_range dcdc4_ranges[] = {
+static const struct linear_range dcdc4_ranges[] = {
        REGULATOR_LINEAR_RANGE(1175000, 0x0, 0xf, 25000),
        REGULATOR_LINEAR_RANGE(1600000, 0x10, 0x34, 50000),
 };
index 276faeddc370d6ee043f6ec7c6f973a0bd54cce7..15c79931ea89b215f96489bbc81122eebc5cf8f0 100644 (file)
@@ -46,11 +46,11 @@ enum tps65912_regulators { DCDC1, DCDC2, DCDC3, DCDC4, LDO1, LDO2, LDO3,
                .n_linear_ranges        = ARRAY_SIZE(_lr),              \
        }
 
-static const struct regulator_linear_range tps65912_dcdc_ranges[] = {
+static const struct linear_range tps65912_dcdc_ranges[] = {
        REGULATOR_LINEAR_RANGE(500000, 0x0, 0x3f, 50000),
 };
 
-static const struct regulator_linear_range tps65912_ldo_ranges[] = {
+static const struct linear_range tps65912_ldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 25000),
        REGULATOR_LINEAR_RANGE(1650000, 0x21, 0x3c, 50000),
        REGULATOR_LINEAR_RANGE(3100000, 0x3d, 0x3f, 100000),
index 85a6a8ca8c1b83380855fcf57aedf78b52600f71..a29e65230132c5e643c21e848850e5ad3da6360b 100644 (file)
@@ -271,7 +271,7 @@ static int tps80031_vbus_is_enabled(struct regulator_dev *rdev)
 {
        struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
        struct device *parent = to_tps80031_dev(rdev);
-       int ret = -EIO;
+       int ret;
        uint8_t ctrl1 = 0;
        uint8_t ctrl3 = 0;
 
@@ -322,7 +322,7 @@ static int tps80031_vbus_disable(struct regulator_dev *rdev)
 {
        struct tps80031_regulator *ri = rdev_get_drvdata(rdev);
        struct device *parent = to_tps80031_dev(rdev);
-       int ret = 0;
+       int ret;
 
        if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) {
                ret = tps80031_write(parent, TPS80031_SLAVE_ID2,
@@ -530,7 +530,8 @@ static int tps80031_regulator_config(struct device *parent,
        case TPS80031_REGULATOR_LDOUSB:
                if (ri->config_flags & (TPS80031_USBLDO_INPUT_VSYS |
                        TPS80031_USBLDO_INPUT_PMID)) {
-                       unsigned val = 0;
+                       unsigned val;
+
                        if (ri->config_flags & TPS80031_USBLDO_INPUT_VSYS)
                                val = MISC2_LDOUSB_IN_VSYS;
                        else
index 866b4dd01da9ea0911da113969f3c2a453aff6d0..4a51cfea45ac2b39efc1abc9fcd2673ffc36b1f2 100644 (file)
@@ -360,12 +360,12 @@ static const u16 VINTANA2_VSEL_table[] = {
 };
 
 /* 600mV to 1450mV in 12.5 mV steps */
-static const struct regulator_linear_range VDD1_ranges[] = {
+static const struct linear_range VDD1_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 68, 12500)
 };
 
 /* 600mV to 1450mV in 12.5 mV steps, everything above = 1500mV */
-static const struct regulator_linear_range VDD2_ranges[] = {
+static const struct linear_range VDD2_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 68, 12500),
        REGULATOR_LINEAR_RANGE(1500000, 69, 69, 12500)
 };
index b8100c3cedad3fc91bebe9c2f09fb09b85d15b28..f7db250a75839ece685010fae72817613dadaddb 100644 (file)
@@ -495,7 +495,7 @@ static const struct regulator_ops twlsmps_ops = {
 };
 
 /*----------------------------------------------------------------------*/
-static const struct regulator_linear_range twl6030ldo_linear_range[] = {
+static const struct linear_range twl6030ldo_linear_range[] = {
        REGULATOR_LINEAR_RANGE(0, 0, 0, 0),
        REGULATOR_LINEAR_RANGE(1000000, 1, 24, 100000),
        REGULATOR_LINEAR_RANGE(2750000, 31, 31, 0),
index 018dbbd9677101b38cb9b9befd5b2134f5dcace9..ad2203d11a8804cb3c6b2c4917fa95e1ba781028 100644 (file)
@@ -204,7 +204,7 @@ static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data)
  * BUCKV specifics
  */
 
-static const struct regulator_linear_range wm831x_buckv_ranges[] = {
+static const struct linear_range wm831x_buckv_ranges[] = {
        REGULATOR_LINEAR_RANGE(600000, 0, 0x7, 0),
        REGULATOR_LINEAR_RANGE(600000, 0x8, 0x68, 12500),
 };
index 56754686c982afac38d0fb0e0c40dae1c4cf13b2..7b6cf4810cb72f5b8a5d01996845d782acb54107 100644 (file)
@@ -59,7 +59,7 @@ static irqreturn_t wm831x_ldo_uv_irq(int irq, void *data)
  * General purpose LDOs
  */
 
-static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = {
+static const struct linear_range wm831x_gp_ldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
        REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
 };
@@ -312,7 +312,7 @@ static struct platform_driver wm831x_gp_ldo_driver = {
  * Analogue LDOs
  */
 
-static const struct regulator_linear_range wm831x_aldo_ranges[] = {
+static const struct linear_range wm831x_aldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(1000000, 0, 12, 50000),
        REGULATOR_LINEAR_RANGE(1700000, 13, 31, 100000),
 };
index 56d6168a888d1181c5943b88b9132497b4436c63..ae5f0e7fce8bbb0d03ed06d5a6f907903e8c00e7 100644 (file)
@@ -470,7 +470,7 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev,
        return 0;
 }
 
-static const struct regulator_linear_range wm8350_ldo_ranges[] = {
+static const struct linear_range wm8350_ldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 15, 50000),
        REGULATOR_LINEAR_RANGE(1800000, 16, 31, 100000),
 };
index 6f331b51e47991054f13557f18a7503290be5fc7..4cb1fbb597227de51fba67edd6ef8964a62c47bb 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/mfd/wm8400-private.h>
 
-static const struct regulator_linear_range wm8400_ldo_ranges[] = {
+static const struct linear_range wm8400_ldo_ranges[] = {
        REGULATOR_LINEAR_RANGE(900000, 0, 14, 50000),
        REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
 };
index 442c5e70a7b484f0e9ade6dbd990e153ce9218b0..cc620f10eabc4a77ea3b7904f230a41b1b581eaf 100644 (file)
@@ -1510,7 +1510,7 @@ ioctl_done:
        }
 
        /* Always copy the buffer back, if only to pick up the status */
-       err = __copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
+       err = copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
        if (err != 0) {
                esas2r_log(ESAS2R_LOG_WARN,
                           "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)",
index 8a6e02aa553fc17558e9d815a970d6f887d66acb..5a754fb5f85469b73ba42016441f44783a6b5e91 100644 (file)
@@ -2166,10 +2166,6 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
        char *pbuf;
        int i;
 
-       /* Protect copy from user */
-       if (!access_ok(buf, nbytes))
-               return -EFAULT;
-
        memset(mybuf, 0, sizeof(mybuf));
 
        if (copy_from_user(mybuf, buf, nbytes))
@@ -2621,10 +2617,6 @@ lpfc_debugfs_multixripools_write(struct file *file, const char __user *buf,
        if (nbytes > 64)
                nbytes = 64;
 
-       /* Protect copy from user */
-       if (!access_ok(buf, nbytes))
-               return -EFAULT;
-
        memset(mybuf, 0, sizeof(mybuf));
 
        if (copy_from_user(mybuf, buf, nbytes))
@@ -2787,10 +2779,6 @@ lpfc_debugfs_scsistat_write(struct file *file, const char __user *buf,
        char mybuf[6] = {0};
        int i;
 
-       /* Protect copy from user */
-       if (!access_ok(buf, nbytes))
-               return -EFAULT;
-
        if (copy_from_user(mybuf, buf, (nbytes >= sizeof(mybuf)) ?
                                       (sizeof(mybuf) - 1) : nbytes))
                return -EFAULT;
index 8f3af87b6bb0c3a1f36ec0e45dc574cc4e40df14..45d04b7b26439896b3caa4654222d77255994750 100644 (file)
@@ -211,18 +211,18 @@ static int scsi_ioctl_common(struct scsi_device *sdev, int cmd, void __user *arg
        }
 
        switch (cmd) {
-       case SCSI_IOCTL_GET_IDLUN:
-               if (!access_ok(arg, sizeof(struct scsi_idlun)))
+       case SCSI_IOCTL_GET_IDLUN: {
+               struct scsi_idlun v = {
+                       .dev_id = (sdev->id & 0xff)
+                                + ((sdev->lun & 0xff) << 8)
+                                + ((sdev->channel & 0xff) << 16)
+                                + ((sdev->host->host_no & 0xff) << 24),
+                       .host_unique_id = sdev->host->unique_id
+               };
+               if (copy_to_user(arg, &v, sizeof(struct scsi_idlun)))
                        return -EFAULT;
-
-               __put_user((sdev->id & 0xff)
-                        + ((sdev->lun & 0xff) << 8)
-                        + ((sdev->channel & 0xff) << 16)
-                        + ((sdev->host->host_no & 0xff) << 24),
-                        &((struct scsi_idlun __user *)arg)->dev_id);
-               __put_user(sdev->host->unique_id,
-                        &((struct scsi_idlun __user *)arg)->host_unique_id);
                return 0;
+       }
        case SCSI_IOCTL_GET_BUS_NUMBER:
                return put_user(sdev->host->host_no, (int __user *)arg);
        case SCSI_IOCTL_PROBE_HOST:
index f45c22b097269b555255731065d301f6a47b7315..8be27426aa66f0b340ab4c08e7ab75293b2cc94c 100644 (file)
@@ -136,8 +136,7 @@ static void *sd_zbc_alloc_report_buffer(struct scsi_disk *sdkp,
 
        while (bufsize >= SECTOR_SIZE) {
                buf = __vmalloc(bufsize,
-                               GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY,
-                               PAGE_KERNEL);
+                               GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY);
                if (buf) {
                        *buflen = bufsize;
                        return buf;
index db37144ae98c69a8ae0cb20ca8695c5150e53346..87ee9f767b7aff873a3e4284a91abaf90ac31ad2 100644 (file)
@@ -351,7 +351,9 @@ int cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb,
                spin_unlock_irqrestore(&client->lock, flags);
        }
 
-       mbox_send_message(client->chan, pkt);
+       err = mbox_send_message(client->chan, pkt);
+       if (err < 0)
+               return err;
        /* We can send next packet immediately, so just call txdone. */
        mbox_client_txdone(client->chan, 0);
 
index bf42a17a45def792652fab0b9af9c857534017e3..285baa7e474e0fc6bc0fd739821f02dc23d2308b 100644 (file)
@@ -80,16 +80,6 @@ config QCOM_PDR_HELPERS
        tristate
        select QCOM_QMI_HELPERS
 
-config QCOM_PM
-       bool "Qualcomm Power Management"
-       depends on ARCH_QCOM && !ARM64
-       select ARM_CPU_SUSPEND
-       select QCOM_SCM
-       help
-         QCOM Platform specific power driver to manage cores and L2 low power
-         modes. It interface with various system drivers to put the cores in
-         low power modes.
-
 config QCOM_QMI_HELPERS
        tristate
        depends on NET
index 5d6b83dc58e8272ca2da3dfcde78d842b0ed8781..92cc4232d72ce166bbdda824ff19e2adf40fbb85 100644 (file)
@@ -8,7 +8,6 @@ obj-$(CONFIG_QCOM_GSBI) +=      qcom_gsbi.o
 obj-$(CONFIG_QCOM_MDT_LOADER)  += mdt_loader.o
 obj-$(CONFIG_QCOM_OCMEM)       += ocmem.o
 obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o
-obj-$(CONFIG_QCOM_PM)  +=      spm.o
 obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
 qmi_helpers-y  += qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)   += rmtfs_mem.o
index 741b9140992a83772d77318e22600dbd43828d99..8f1f8fca79e377b2df1d60a1c40d2c24843a2e82 100644 (file)
@@ -226,17 +226,20 @@ config SPI_DESIGNWARE
        help
          general driver for SPI controller core from DesignWare
 
+if SPI_DESIGNWARE
+
+config SPI_DW_DMA
+       bool "DMA support for DW SPI controller"
+
 config SPI_DW_PCI
        tristate "PCI interface driver for DW SPI core"
-       depends on SPI_DESIGNWARE && PCI
-
-config SPI_DW_MID_DMA
-       bool "DMA support for DW SPI controller on Intel MID platform"
-       depends on SPI_DW_PCI && DW_DMAC_PCI
+       depends on PCI
 
 config SPI_DW_MMIO
        tristate "Memory-mapped io interface driver for DW SPI core"
-       depends on SPI_DESIGNWARE
+       depends on HAS_IOMEM
+
+endif
 
 config SPI_DLN2
        tristate "Diolan DLN-2 USB SPI adapter"
@@ -844,6 +847,7 @@ config SPI_TXX9
 config SPI_UNIPHIER
        tristate "Socionext UniPhier SPI Controller"
        depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+       depends on HAS_IOMEM
        help
          This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller.
 
@@ -910,6 +914,12 @@ config SPI_ZYNQMP_GQSPI
        help
          Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
 
+config SPI_AMD
+       tristate "AMD SPI controller"
+       depends on SPI_MASTER || COMPILE_TEST
+       help
+         Enables SPI controller driver for AMD SoC.
+
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
index 28f601327f8c7a9a8ae29ff4aee1e847790936fa..d2e41d3d464a2e18aefb9d05bb4ec4bd15900ebe 100644 (file)
@@ -36,9 +36,10 @@ obj-$(CONFIG_SPI_COLDFIRE_QSPI)              += spi-coldfire-qspi.o
 obj-$(CONFIG_SPI_DAVINCI)              += spi-davinci.o
 obj-$(CONFIG_SPI_DLN2)                 += spi-dln2.o
 obj-$(CONFIG_SPI_DESIGNWARE)           += spi-dw.o
+spi-dw-y                               := spi-dw-core.o
+spi-dw-$(CONFIG_SPI_DW_DMA)            += spi-dw-dma.o
 obj-$(CONFIG_SPI_DW_MMIO)              += spi-dw-mmio.o
-obj-$(CONFIG_SPI_DW_PCI)               += spi-dw-midpci.o
-spi-dw-midpci-objs                     := spi-dw-pci.o spi-dw-mid.o
+obj-$(CONFIG_SPI_DW_PCI)               += spi-dw-pci.o
 obj-$(CONFIG_SPI_EFM32)                        += spi-efm32.o
 obj-$(CONFIG_SPI_EP93XX)               += spi-ep93xx.o
 obj-$(CONFIG_SPI_FALCON)               += spi-falcon.o
@@ -127,6 +128,7 @@ obj-$(CONFIG_SPI_XLP)                       += spi-xlp.o
 obj-$(CONFIG_SPI_XTENSA_XTFPGA)                += spi-xtensa-xtfpga.o
 obj-$(CONFIG_SPI_ZYNQ_QSPI)            += spi-zynq-qspi.o
 obj-$(CONFIG_SPI_ZYNQMP_GQSPI)         += spi-zynqmp-gqspi.o
+obj-$(CONFIG_SPI_AMD)                  += spi-amd.o
 
 # SPI slave protocol handlers
 obj-$(CONFIG_SPI_SLAVE_TIME)           += spi-slave-time.o
diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c
new file mode 100644 (file)
index 0000000..d0aacd4
--- /dev/null
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// AMD SPI controller driver
+//
+// Copyright (c) 2020, Advanced Micro Devices, Inc.
+//
+// Author: Sanjay R Mehta <sanju.mehta@amd.com>
+
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#define AMD_SPI_CTRL0_REG      0x00
+#define AMD_SPI_EXEC_CMD       BIT(16)
+#define AMD_SPI_FIFO_CLEAR     BIT(20)
+#define AMD_SPI_BUSY           BIT(31)
+
+#define AMD_SPI_OPCODE_MASK    0xFF
+
+#define AMD_SPI_ALT_CS_REG     0x1D
+#define AMD_SPI_ALT_CS_MASK    0x3
+
+#define AMD_SPI_FIFO_BASE      0x80
+#define AMD_SPI_TX_COUNT_REG   0x48
+#define AMD_SPI_RX_COUNT_REG   0x4B
+#define AMD_SPI_STATUS_REG     0x4C
+
+#define AMD_SPI_MEM_SIZE       200
+
+/* M_CMD OP codes for SPI */
+#define AMD_SPI_XFER_TX                1
+#define AMD_SPI_XFER_RX                2
+
+struct amd_spi {
+       void __iomem *io_remap_addr;
+       unsigned long io_base_addr;
+       u32 rom_addr;
+       u8 chip_select;
+};
+
+static inline u8 amd_spi_readreg8(struct spi_master *master, int idx)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+
+       return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx);
+}
+
+static inline void amd_spi_writereg8(struct spi_master *master, int idx,
+                                    u8 val)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+
+       iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
+}
+
+static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx,
+                                        u8 set, u8 clear)
+{
+       u8 tmp = amd_spi_readreg8(master, idx);
+
+       tmp = (tmp & ~clear) | set;
+       amd_spi_writereg8(master, idx, tmp);
+}
+
+static inline u32 amd_spi_readreg32(struct spi_master *master, int idx)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+
+       return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx);
+}
+
+static inline void amd_spi_writereg32(struct spi_master *master, int idx,
+                                     u32 val)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+
+       iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
+}
+
+static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx,
+                                         u32 set, u32 clear)
+{
+       u32 tmp = amd_spi_readreg32(master, idx);
+
+       tmp = (tmp & ~clear) | set;
+       amd_spi_writereg32(master, idx, tmp);
+}
+
+static void amd_spi_select_chip(struct spi_master *master)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+       u8 chip_select = amd_spi->chip_select;
+
+       amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select,
+                             AMD_SPI_ALT_CS_MASK);
+}
+
+static void amd_spi_clear_fifo_ptr(struct spi_master *master)
+{
+       amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR,
+                              AMD_SPI_FIFO_CLEAR);
+}
+
+static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode)
+{
+       amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode,
+                              AMD_SPI_OPCODE_MASK);
+}
+
+static inline void amd_spi_set_rx_count(struct spi_master *master,
+                                       u8 rx_count)
+{
+       amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff);
+}
+
+static inline void amd_spi_set_tx_count(struct spi_master *master,
+                                       u8 tx_count)
+{
+       amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff);
+}
+
+static inline int amd_spi_busy_wait(struct amd_spi *amd_spi)
+{
+       bool spi_busy;
+       int timeout = 100000;
+
+       /* poll for SPI bus to become idle */
+       spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr +
+                   AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY;
+       while (spi_busy) {
+               usleep_range(10, 20);
+               if (timeout-- < 0)
+                       return -ETIMEDOUT;
+
+               spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr +
+                           AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY;
+       }
+
+       return 0;
+}
+
+static void amd_spi_execute_opcode(struct spi_master *master)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+
+       /* Set ExecuteOpCode bit in the CTRL0 register */
+       amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD,
+                              AMD_SPI_EXEC_CMD);
+
+       amd_spi_busy_wait(amd_spi);
+}
+
+static int amd_spi_master_setup(struct spi_device *spi)
+{
+       struct spi_master *master = spi->master;
+
+       amd_spi_clear_fifo_ptr(master);
+
+       return 0;
+}
+
+static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
+                                   struct spi_master *master,
+                                   struct spi_message *message)
+{
+       struct spi_transfer *xfer = NULL;
+       u8 cmd_opcode;
+       u8 *buf = NULL;
+       u32 m_cmd = 0;
+       u32 i = 0;
+       u32 tx_len = 0, rx_len = 0;
+
+       list_for_each_entry(xfer, &message->transfers,
+                           transfer_list) {
+               if (xfer->rx_buf)
+                       m_cmd = AMD_SPI_XFER_RX;
+               if (xfer->tx_buf)
+                       m_cmd = AMD_SPI_XFER_TX;
+
+               if (m_cmd & AMD_SPI_XFER_TX) {
+                       buf = (u8 *)xfer->tx_buf;
+                       tx_len = xfer->len - 1;
+                       cmd_opcode = *(u8 *)xfer->tx_buf;
+                       buf++;
+                       amd_spi_set_opcode(master, cmd_opcode);
+
+                       /* Write data into the FIFO. */
+                       for (i = 0; i < tx_len; i++) {
+                               iowrite8(buf[i],
+                                        ((u8 __iomem *)amd_spi->io_remap_addr +
+                                        AMD_SPI_FIFO_BASE + i));
+                       }
+
+                       amd_spi_set_tx_count(master, tx_len);
+                       amd_spi_clear_fifo_ptr(master);
+                       /* Execute command */
+                       amd_spi_execute_opcode(master);
+               }
+               if (m_cmd & AMD_SPI_XFER_RX) {
+                       /*
+                        * Store no. of bytes to be received from
+                        * FIFO
+                        */
+                       rx_len = xfer->len;
+                       buf = (u8 *)xfer->rx_buf;
+                       amd_spi_set_rx_count(master, rx_len);
+                       amd_spi_clear_fifo_ptr(master);
+                       /* Execute command */
+                       amd_spi_execute_opcode(master);
+                       /* Read data from FIFO to receive buffer  */
+                       for (i = 0; i < rx_len; i++)
+                               buf[i] = amd_spi_readreg8(master,
+                                                         AMD_SPI_FIFO_BASE +
+                                                         tx_len + i);
+               }
+       }
+
+       /* Update statistics */
+       message->actual_length = tx_len + rx_len + 1;
+       /* complete the transaction */
+       message->status = 0;
+       spi_finalize_current_message(master);
+
+       return 0;
+}
+
+static int amd_spi_master_transfer(struct spi_master *master,
+                                  struct spi_message *msg)
+{
+       struct amd_spi *amd_spi = spi_master_get_devdata(master);
+       struct spi_device *spi = msg->spi;
+
+       amd_spi->chip_select = spi->chip_select;
+       amd_spi_select_chip(master);
+
+       /*
+        * Extract spi_transfers from the spi message and
+        * program the controller.
+        */
+       amd_spi_fifo_xfer(amd_spi, master, msg);
+
+       return 0;
+}
+
+static int amd_spi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct spi_master *master;
+       struct amd_spi *amd_spi;
+       struct resource *res;
+       int err = 0;
+
+       /* Allocate storage for spi_master and driver private data */
+       master = spi_alloc_master(dev, sizeof(struct amd_spi));
+       if (!master) {
+               dev_err(dev, "Error allocating SPI master\n");
+               return -ENOMEM;
+       }
+
+       amd_spi = spi_master_get_devdata(master);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(amd_spi->io_remap_addr)) {
+               err = PTR_ERR(amd_spi->io_remap_addr);
+               dev_err(dev, "error %d ioremap of SPI registers failed\n", err);
+               goto err_free_master;
+       }
+       dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
+
+       /* Initialize the spi_master fields */
+       master->bus_num = 0;
+       master->num_chipselect = 4;
+       master->mode_bits = 0;
+       master->flags = SPI_MASTER_HALF_DUPLEX;
+       master->setup = amd_spi_master_setup;
+       master->transfer_one_message = amd_spi_master_transfer;
+
+       /* Register the controller with SPI framework */
+       err = devm_spi_register_master(dev, master);
+       if (err) {
+               dev_err(dev, "error %d registering SPI controller\n", err);
+               goto err_free_master;
+       }
+
+       return 0;
+
+err_free_master:
+       spi_master_put(master);
+
+       return err;
+}
+
+static const struct acpi_device_id spi_acpi_match[] = {
+       { "AMDI0061", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, spi_acpi_match);
+
+static struct platform_driver amd_spi_driver = {
+       .driver = {
+               .name = "amd_spi",
+               .acpi_match_table = ACPI_PTR(spi_acpi_match),
+       },
+       .probe = amd_spi_probe,
+};
+
+module_platform_driver(amd_spi_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>");
+MODULE_DESCRIPTION("AMD SPI Master Controller Driver");
index e450ee17787f0ecebb2c88e262722ea088736563..fcde419e480cfca490c1e0b08bc9ae2e12670923 100644 (file)
@@ -276,11 +276,11 @@ static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi)
        return -ETIMEDOUT;
 }
 
-static int a3700_spi_init(struct a3700_spi *a3700_spi)
+static void a3700_spi_init(struct a3700_spi *a3700_spi)
 {
        struct spi_master *master = a3700_spi->master;
        u32 val;
-       int i, ret = 0;
+       int i;
 
        /* Reset SPI unit */
        val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
@@ -311,8 +311,6 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi)
        /* Mask the interrupts and clear cause bits */
        spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
        spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U);
-
-       return ret;
 }
 
 static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id)
@@ -886,9 +884,7 @@ static int a3700_spi_probe(struct platform_device *pdev)
        master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk),
                                                A3700_SPI_MAX_PRESCALE);
 
-       ret = a3700_spi_init(spi);
-       if (ret)
-               goto error_clk;
+       a3700_spi_init(spi);
 
        ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0,
                               dev_name(dev), master);
index 013458cabe3c6f05e2b47fa4384d87295ab4206b..57ee8c3b7972c005b3e56e63a84260b5d45d90fb 100644 (file)
@@ -706,6 +706,7 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
 static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
                                struct spi_transfer *xfer,
                                u32 *plen)
+       __must_hold(&as->lock)
 {
        struct atmel_spi        *as = spi_master_get_devdata(master);
        struct dma_chan         *rxchan = master->dma_rx;
index eb9b78a90dcf869e4600264f4c2e4e90ad1fd8b4..af86e6d6e16b9b9ce17466e2b86c045eabccf07f 100644 (file)
@@ -489,22 +489,6 @@ static int spi_engine_probe(struct platform_device *pdev)
 
        spin_lock_init(&spi_engine->lock);
 
-       spi_engine->base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(spi_engine->base)) {
-               ret = PTR_ERR(spi_engine->base);
-               goto err_put_master;
-       }
-
-       version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION);
-       if (SPI_ENGINE_VERSION_MAJOR(version) != 1) {
-               dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n",
-                       SPI_ENGINE_VERSION_MAJOR(version),
-                       SPI_ENGINE_VERSION_MINOR(version),
-                       SPI_ENGINE_VERSION_PATCH(version));
-               ret = -ENODEV;
-               goto err_put_master;
-       }
-
        spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
        if (IS_ERR(spi_engine->clk)) {
                ret = PTR_ERR(spi_engine->clk);
@@ -525,6 +509,22 @@ static int spi_engine_probe(struct platform_device *pdev)
        if (ret)
                goto err_clk_disable;
 
+       spi_engine->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(spi_engine->base)) {
+               ret = PTR_ERR(spi_engine->base);
+               goto err_ref_clk_disable;
+       }
+
+       version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION);
+       if (SPI_ENGINE_VERSION_MAJOR(version) != 1) {
+               dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n",
+                       SPI_ENGINE_VERSION_MAJOR(version),
+                       SPI_ENGINE_VERSION_MINOR(version),
+                       SPI_ENGINE_VERSION_PATCH(version));
+               ret = -ENODEV;
+               goto err_ref_clk_disable;
+       }
+
        writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
        writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
        writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
index 23d295f36c80dad3daa1ea3a67e24a8ca5f1d2d6..681d09085175696ccc9f3db3e332e2bdd3650558 100644 (file)
@@ -91,6 +91,7 @@
 #define MSPI_MSPI_STATUS                       0x020
 #define MSPI_CPTQP                             0x024
 #define MSPI_SPCR3                             0x028
+#define MSPI_REV                               0x02c
 #define MSPI_TXRAM                             0x040
 #define MSPI_RXRAM                             0x0c0
 #define MSPI_CDRAM                             0x140
 #define MSPI_SPCR2_SPE                         BIT(6)
 #define MSPI_SPCR2_CONT_AFTER_CMD              BIT(7)
 
+#define MSPI_SPCR3_FASTBR                      BIT(0)
+#define MSPI_SPCR3_FASTDT                      BIT(1)
+#define MSPI_SPCR3_SYSCLKSEL_MASK              GENMASK(11, 10)
+#define MSPI_SPCR3_SYSCLKSEL_27                        (MSPI_SPCR3_SYSCLKSEL_MASK & \
+                                                ~(BIT(10) | BIT(11)))
+#define MSPI_SPCR3_SYSCLKSEL_108               (MSPI_SPCR3_SYSCLKSEL_MASK & \
+                                                BIT(11))
+
 #define MSPI_MSPI_STATUS_SPIF                  BIT(0)
 
 #define INTR_BASE_BIT_SHIFT                    0x02
 #define INTR_COUNT                             0x07
 
 #define NUM_CHIPSELECT                         4
-#define QSPI_SPBR_MIN                          8U
 #define QSPI_SPBR_MAX                          255U
+#define MSPI_BASE_FREQ                         27000000UL
 
 #define OPCODE_DIOR                            0xBB
 #define OPCODE_QIOR                            0xEB
@@ -217,6 +226,9 @@ struct bcm_qspi {
        struct bcm_qspi_dev_id *dev_ids;
        struct completion mspi_done;
        struct completion bspi_done;
+       u8 mspi_maj_rev;
+       u8 mspi_min_rev;
+       bool mspi_spcr3_sysclk;
 };
 
 static inline bool has_bspi(struct bcm_qspi *qspi)
@@ -224,6 +236,36 @@ static inline bool has_bspi(struct bcm_qspi *qspi)
        return qspi->bspi_mode;
 }
 
+/* hardware supports spcr3 and fast baud-rate  */
+static inline bool bcm_qspi_has_fastbr(struct bcm_qspi *qspi)
+{
+       if (!has_bspi(qspi) &&
+           ((qspi->mspi_maj_rev >= 1) &&
+            (qspi->mspi_min_rev >= 5)))
+               return true;
+
+       return false;
+}
+
+/* hardware supports sys clk 108Mhz  */
+static inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi)
+{
+       if (!has_bspi(qspi) && (qspi->mspi_spcr3_sysclk ||
+           ((qspi->mspi_maj_rev >= 1) &&
+            (qspi->mspi_min_rev >= 6))))
+               return true;
+
+       return false;
+}
+
+static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi)
+{
+       if (bcm_qspi_has_fastbr(qspi))
+               return 1;
+       else
+               return 8;
+}
+
 /* Read qspi controller register*/
 static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type,
                                unsigned int offset)
@@ -531,16 +573,39 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi,
        if (xp->speed_hz)
                spbr = qspi->base_clk / (2 * xp->speed_hz);
 
-       spcr = clamp_val(spbr, QSPI_SPBR_MIN, QSPI_SPBR_MAX);
+       spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX);
        bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr);
 
-       spcr = MSPI_MASTER_BIT;
+       if (!qspi->mspi_maj_rev)
+               /* legacy controller */
+               spcr = MSPI_MASTER_BIT;
+       else
+               spcr = 0;
+
        /* for 16 bit the data should be zero */
        if (xp->bits_per_word != 16)
                spcr |= xp->bits_per_word << 2;
        spcr |= xp->mode & 3;
+
        bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr);
 
+       if (bcm_qspi_has_fastbr(qspi)) {
+               spcr = 0;
+
+               /* enable fastbr */
+               spcr |= MSPI_SPCR3_FASTBR;
+
+               if (bcm_qspi_has_sysclk_108(qspi)) {
+                       /* SYSCLK_108 */
+                       spcr |= MSPI_SPCR3_SYSCLKSEL_108;
+                       qspi->base_clk = MSPI_BASE_FREQ * 4;
+                       /* Change spbr as we changed sysclk */
+                       bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4);
+               }
+
+               bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr);
+       }
+
        qspi->last_parms = *xp;
 }
 
@@ -612,19 +677,15 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
                if (qt->trans->cs_change &&
                    (flags & TRANS_STATUS_BREAK_CS_CHANGE))
                        ret |= TRANS_STATUS_BREAK_CS_CHANGE;
-               if (ret)
-                       goto done;
 
-               dev_dbg(&qspi->pdev->dev, "advance msg exit\n");
                if (bcm_qspi_mspi_transfer_is_last(qspi, qt))
-                       ret = TRANS_STATUS_BREAK_EOM;
+                       ret |= TRANS_STATUS_BREAK_EOM;
                else
-                       ret = TRANS_STATUS_BREAK_NO_BYTES;
+                       ret |= TRANS_STATUS_BREAK_NO_BYTES;
 
                qt->trans = NULL;
        }
 
-done:
        dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n",
                qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret);
        return ret;
@@ -670,7 +731,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots)
                        if (buf)
                                buf[tp.byte] = read_rxram_slot_u8(qspi, slot);
                        dev_dbg(&qspi->pdev->dev, "RD %02x\n",
-                               buf ? buf[tp.byte] : 0xff);
+                               buf ? buf[tp.byte] : 0x0);
                } else {
                        u16 *buf = tp.trans->rx_buf;
 
@@ -678,7 +739,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots)
                                buf[tp.byte / 2] = read_rxram_slot_u16(qspi,
                                                                      slot);
                        dev_dbg(&qspi->pdev->dev, "RD %04x\n",
-                               buf ? buf[tp.byte] : 0xffff);
+                               buf ? buf[tp.byte / 2] : 0x0);
                }
 
                update_qspi_trans_byte_count(qspi, &tp,
@@ -733,13 +794,13 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
        while (!tstatus && slot < MSPI_NUM_CDRAM) {
                if (tp.trans->bits_per_word <= 8) {
                        const u8 *buf = tp.trans->tx_buf;
-                       u8 val = buf ? buf[tp.byte] : 0xff;
+                       u8 val = buf ? buf[tp.byte] : 0x00;
 
                        write_txram_slot_u8(qspi, slot, val);
                        dev_dbg(&qspi->pdev->dev, "WR %02x\n", val);
                } else {
                        const u16 *buf = tp.trans->tx_buf;
-                       u16 val = buf ? buf[tp.byte / 2] : 0xffff;
+                       u16 val = buf ? buf[tp.byte / 2] : 0x0000;
 
                        write_txram_slot_u16(qspi, slot, val);
                        dev_dbg(&qspi->pdev->dev, "WR %04x\n", val);
@@ -771,7 +832,16 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
        bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0);
        bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1);
 
-       if (tstatus & TRANS_STATUS_BREAK_DESELECT) {
+       /*
+        *  case 1) EOM =1, cs_change =0: SSb inactive
+        *  case 2) EOM =1, cs_change =1: SSb stay active
+        *  case 3) EOM =0, cs_change =0: SSb stay active
+        *  case 4) EOM =0, cs_change =1: SSb inactive
+        */
+       if (((tstatus & TRANS_STATUS_BREAK_DESELECT)
+            == TRANS_STATUS_BREAK_CS_CHANGE) ||
+           ((tstatus & TRANS_STATUS_BREAK_DESELECT)
+            == TRANS_STATUS_BREAK_EOM)) {
                mspi_cdram = read_cdram_slot(qspi, slot - 1) &
                        ~MSPI_CDRAM_CONT_BIT;
                write_cdram_slot(qspi, slot - 1, mspi_cdram);
@@ -1190,8 +1260,51 @@ static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
        .exec_op = bcm_qspi_exec_mem_op,
 };
 
+struct bcm_qspi_data {
+       bool    has_mspi_rev;
+       bool    has_spcr3_sysclk;
+};
+
+static const struct bcm_qspi_data bcm_qspi_no_rev_data = {
+       .has_mspi_rev   = false,
+       .has_spcr3_sysclk = false,
+};
+
+static const struct bcm_qspi_data bcm_qspi_rev_data = {
+       .has_mspi_rev   = true,
+       .has_spcr3_sysclk = false,
+};
+
+static const struct bcm_qspi_data bcm_qspi_spcr3_data = {
+       .has_mspi_rev   = true,
+       .has_spcr3_sysclk = true,
+};
+
 static const struct of_device_id bcm_qspi_of_match[] = {
-       { .compatible = "brcm,spi-bcm-qspi" },
+       {
+               .compatible = "brcm,spi-bcm7425-qspi",
+               .data = &bcm_qspi_no_rev_data,
+       },
+       {
+               .compatible = "brcm,spi-bcm7429-qspi",
+               .data = &bcm_qspi_no_rev_data,
+       },
+       {
+               .compatible = "brcm,spi-bcm7435-qspi",
+               .data = &bcm_qspi_no_rev_data,
+       },
+       {
+               .compatible = "brcm,spi-bcm-qspi",
+               .data = &bcm_qspi_rev_data,
+       },
+       {
+               .compatible = "brcm,spi-bcm7216-qspi",
+               .data = &bcm_qspi_spcr3_data,
+       },
+       {
+               .compatible = "brcm,spi-bcm7278-qspi",
+               .data = &bcm_qspi_spcr3_data,
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, bcm_qspi_of_match);
@@ -1199,12 +1312,15 @@ MODULE_DEVICE_TABLE(of, bcm_qspi_of_match);
 int bcm_qspi_probe(struct platform_device *pdev,
                   struct bcm_qspi_soc_intc *soc_intc)
 {
+       const struct of_device_id *of_id = NULL;
+       const struct bcm_qspi_data *data;
        struct device *dev = &pdev->dev;
        struct bcm_qspi *qspi;
        struct spi_master *master;
        struct resource *res;
        int irq, ret = 0, num_ints = 0;
        u32 val;
+       u32 rev = 0;
        const char *name = NULL;
        int num_irqs = ARRAY_SIZE(qspi_irq_tab);
 
@@ -1212,9 +1328,12 @@ int bcm_qspi_probe(struct platform_device *pdev,
        if (!dev->of_node)
                return -ENODEV;
 
-       if (!of_match_node(bcm_qspi_of_match, dev->of_node))
+       of_id = of_match_node(bcm_qspi_of_match, dev->of_node);
+       if (!of_id)
                return -ENODEV;
 
+       data = of_id->data;
+
        master = spi_alloc_master(dev, sizeof(struct bcm_qspi));
        if (!master) {
                dev_err(dev, "error allocating spi_master\n");
@@ -1222,6 +1341,11 @@ int bcm_qspi_probe(struct platform_device *pdev,
        }
 
        qspi = spi_master_get_devdata(master);
+
+       qspi->clk = devm_clk_get_optional(&pdev->dev, NULL);
+       if (IS_ERR(qspi->clk))
+               return PTR_ERR(qspi->clk);
+
        qspi->pdev = pdev;
        qspi->trans_pos.trans = NULL;
        qspi->trans_pos.byte = 0;
@@ -1335,13 +1459,6 @@ int bcm_qspi_probe(struct platform_device *pdev,
                qspi->soc_intc = NULL;
        }
 
-       qspi->clk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(qspi->clk)) {
-               dev_warn(dev, "unable to get clock\n");
-               ret = PTR_ERR(qspi->clk);
-               goto qspi_probe_err;
-       }
-
        ret = clk_prepare_enable(qspi->clk);
        if (ret) {
                dev_err(dev, "failed to prepare clock\n");
@@ -1349,7 +1466,19 @@ int bcm_qspi_probe(struct platform_device *pdev,
        }
 
        qspi->base_clk = clk_get_rate(qspi->clk);
-       qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2);
+
+       if (data->has_mspi_rev) {
+               rev = bcm_qspi_read(qspi, MSPI, MSPI_REV);
+               /* some older revs do not have a MSPI_REV register */
+               if ((rev & 0xff) == 0xff)
+                       rev = 0;
+       }
+
+       qspi->mspi_maj_rev = (rev >> 4) & 0xf;
+       qspi->mspi_min_rev = rev & 0xf;
+       qspi->mspi_spcr3_sysclk = data->has_spcr3_sysclk;
+
+       qspi->max_speed_hz = qspi->base_clk / (bcm_qspi_spbr_min(qspi) * 2);
 
        bcm_qspi_hw_init(qspi);
        init_completion(&qspi->mspi_done);
@@ -1406,7 +1535,7 @@ static int __maybe_unused bcm_qspi_suspend(struct device *dev)
                        bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
 
        spi_master_suspend(qspi->master);
-       clk_disable(qspi->clk);
+       clk_disable_unprepare(qspi->clk);
        bcm_qspi_hw_uninit(qspi);
 
        return 0;
@@ -1424,7 +1553,7 @@ static int __maybe_unused bcm_qspi_resume(struct device *dev)
                qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE,
                                                 true);
 
-       ret = clk_enable(qspi->clk);
+       ret = clk_prepare_enable(qspi->clk);
        if (!ret)
                spi_master_resume(qspi->master);
 
index 11c235879bb73d80f2656c98523e900009ef84b9..237bd306c26853aa3ba6f1c402455ea9cd18a4dc 100644 (file)
@@ -191,12 +191,12 @@ static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
 }
 #endif /* CONFIG_DEBUG_FS */
 
-static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
+static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned int reg)
 {
        return readl(bs->regs + reg);
 }
 
-static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val)
+static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned int reg, u32 val)
 {
        writel(val, bs->regs + reg);
 }
@@ -940,6 +940,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
        if (dma_mapping_error(ctlr->dma_tx->device->dev, bs->fill_tx_addr)) {
                dev_err(dev, "cannot map zero page - not using DMA mode\n");
                bs->fill_tx_addr = 0;
+               ret = -ENOMEM;
                goto err_release;
        }
 
@@ -949,6 +950,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
                                                     DMA_MEM_TO_DEV, 0);
        if (!bs->fill_tx_desc) {
                dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n");
+               ret = -ENOMEM;
                goto err_release;
        }
 
@@ -979,6 +981,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
        if (dma_mapping_error(ctlr->dma_rx->device->dev, bs->clear_rx_addr)) {
                dev_err(dev, "cannot map clear_rx_cs - not using DMA mode\n");
                bs->clear_rx_addr = 0;
+               ret = -ENOMEM;
                goto err_release;
        }
 
@@ -989,6 +992,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
                                           DMA_MEM_TO_DEV, 0);
                if (!bs->clear_rx_desc[i]) {
                        dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n");
+                       ret = -ENOMEM;
                        goto err_release;
                }
 
@@ -1347,7 +1351,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
                goto out_dma_release;
        }
 
-       err = devm_spi_register_controller(&pdev->dev, ctlr);
+       err = spi_register_controller(ctlr);
        if (err) {
                dev_err(&pdev->dev, "could not register SPI controller: %d\n",
                        err);
@@ -1374,17 +1378,28 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
 
        bcm2835_debugfs_remove(bs);
 
+       spi_unregister_controller(ctlr);
+
+       bcm2835_dma_release(ctlr, bs);
+
        /* Clear FIFOs, and disable the HW block */
        bcm2835_wr(bs, BCM2835_SPI_CS,
                   BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
 
        clk_disable_unprepare(bs->clk);
 
-       bcm2835_dma_release(ctlr, bs);
-
        return 0;
 }
 
+static void bcm2835_spi_shutdown(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = bcm2835_spi_remove(pdev);
+       if (ret)
+               dev_err(&pdev->dev, "failed to shutdown\n");
+}
+
 static const struct of_device_id bcm2835_spi_match[] = {
        { .compatible = "brcm,bcm2835-spi", },
        {}
@@ -1398,6 +1413,7 @@ static struct platform_driver bcm2835_spi_driver = {
        },
        .probe          = bcm2835_spi_probe,
        .remove         = bcm2835_spi_remove,
+       .shutdown       = bcm2835_spi_shutdown,
 };
 module_platform_driver(bcm2835_spi_driver);
 
index a2162ff56a121f1a5072d9c87251c8986cb80521..c331efd6e86b217e8d2957cf95b7135f7ce77ce0 100644 (file)
@@ -569,7 +569,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
                goto out_clk_disable;
        }
 
-       err = devm_spi_register_master(&pdev->dev, master);
+       err = spi_register_master(master);
        if (err) {
                dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
                goto out_clk_disable;
@@ -593,6 +593,8 @@ static int bcm2835aux_spi_remove(struct platform_device *pdev)
 
        bcm2835aux_debugfs_remove(bs);
 
+       spi_unregister_master(master);
+
        bcm2835aux_spi_reset_hw(bs);
 
        /* disable the HW block by releasing the clock */
similarity index 70%
rename from drivers/spi/spi-dw.c
rename to drivers/spi/spi-dw-core.c
index 31e3f866d11a78be9786803548b5f936bfb1e057..323c66c5db506ee2bfedbeea887c54283174b96d 100644 (file)
@@ -24,74 +24,34 @@ struct chip_data {
        u8 tmode;               /* TR/TO/RO/EEPROM */
        u8 type;                /* SPI/SSP/MicroWire */
 
-       u8 poll_mode;           /* 1 means use poll mode */
-
        u16 clk_div;            /* baud rate divider */
        u32 speed_hz;           /* baud rate */
-       void (*cs_control)(u32 command);
 };
 
 #ifdef CONFIG_DEBUG_FS
-#define SPI_REGS_BUFSIZE       1024
-static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf,
-               size_t count, loff_t *ppos)
-{
-       struct dw_spi *dws = file->private_data;
-       char *buf;
-       u32 len = 0;
-       ssize_t ret;
-
-       buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL);
-       if (!buf)
-               return 0;
-
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "%s registers:\n", dev_name(&dws->master->dev));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "=================================\n");
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "CTRL0: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL0));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "CTRL1: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL1));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFLTR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFLTR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR));
-       len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len,
-                       "=================================\n");
-
-       ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-       return ret;
+
+#define DW_SPI_DBGFS_REG(_name, _off)  \
+{                                      \
+       .name = _name,                  \
+       .offset = _off,                 \
 }
 
-static const struct file_operations dw_spi_regs_ops = {
-       .owner          = THIS_MODULE,
-       .open           = simple_open,
-       .read           = dw_spi_show_regs,
-       .llseek         = default_llseek,
+static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = {
+       DW_SPI_DBGFS_REG("CTRLR0", DW_SPI_CTRLR0),
+       DW_SPI_DBGFS_REG("CTRLR1", DW_SPI_CTRLR1),
+       DW_SPI_DBGFS_REG("SSIENR", DW_SPI_SSIENR),
+       DW_SPI_DBGFS_REG("SER", DW_SPI_SER),
+       DW_SPI_DBGFS_REG("BAUDR", DW_SPI_BAUDR),
+       DW_SPI_DBGFS_REG("TXFTLR", DW_SPI_TXFTLR),
+       DW_SPI_DBGFS_REG("RXFTLR", DW_SPI_RXFTLR),
+       DW_SPI_DBGFS_REG("TXFLR", DW_SPI_TXFLR),
+       DW_SPI_DBGFS_REG("RXFLR", DW_SPI_RXFLR),
+       DW_SPI_DBGFS_REG("SR", DW_SPI_SR),
+       DW_SPI_DBGFS_REG("IMR", DW_SPI_IMR),
+       DW_SPI_DBGFS_REG("ISR", DW_SPI_ISR),
+       DW_SPI_DBGFS_REG("DMACR", DW_SPI_DMACR),
+       DW_SPI_DBGFS_REG("DMATDLR", DW_SPI_DMATDLR),
+       DW_SPI_DBGFS_REG("DMARDLR", DW_SPI_DMARDLR),
 };
 
 static int dw_spi_debugfs_init(struct dw_spi *dws)
@@ -103,8 +63,11 @@ static int dw_spi_debugfs_init(struct dw_spi *dws)
        if (!dws->debugfs)
                return -ENOMEM;
 
-       debugfs_create_file("registers", S_IFREG | S_IRUGO,
-               dws->debugfs, (void *)dws, &dw_spi_regs_ops);
+       dws->regset.regs = dw_spi_dbgfs_regs;
+       dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs);
+       dws->regset.base = dws->regs;
+       debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset);
+
        return 0;
 }
 
@@ -127,13 +90,16 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
 void dw_spi_set_cs(struct spi_device *spi, bool enable)
 {
        struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
-       struct chip_data *chip = spi_get_ctldata(spi);
-
-       /* Chip select logic is inverted from spi_set_cs() */
-       if (chip && chip->cs_control)
-               chip->cs_control(!enable);
+       bool cs_high = !!(spi->mode & SPI_CS_HIGH);
 
-       if (!enable)
+       /*
+        * DW SPI controller demands any native CS being set in order to
+        * proceed with data transfer. So in order to activate the SPI
+        * communications we must set a corresponding bit in the Slave
+        * Enable register no matter whether the SPI core is configured to
+        * support active-high or active-low CS level.
+        */
+       if (cs_high == enable)
                dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
        else if (dws->cs_override)
                dw_writel(dws, DW_SPI_SER, 0);
@@ -265,17 +231,56 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
        return dws->transfer_handler(dws);
 }
 
-/* Must be called inside pump_transfers() */
-static int poll_transfer(struct dw_spi *dws)
+/* Configure CTRLR0 for DW_apb_ssi */
+u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi,
+                     struct spi_transfer *transfer)
 {
-       do {
-               dw_writer(dws);
-               dw_reader(dws);
-               cpu_relax();
-       } while (dws->rx_end > dws->rx);
+       struct chip_data *chip = spi_get_ctldata(spi);
+       u32 cr0;
 
-       return 0;
+       /* Default SPI mode is SCPOL = 0, SCPH = 0 */
+       cr0 = (transfer->bits_per_word - 1)
+               | (chip->type << SPI_FRF_OFFSET)
+               | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) |
+                  (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) |
+                  (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET))
+               | (chip->tmode << SPI_TMOD_OFFSET);
+
+       return cr0;
+}
+EXPORT_SYMBOL_GPL(dw_spi_update_cr0);
+
+/* Configure CTRLR0 for DWC_ssi */
+u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master,
+                            struct spi_device *spi,
+                            struct spi_transfer *transfer)
+{
+       struct chip_data *chip = spi_get_ctldata(spi);
+       u32 cr0;
+
+       /* CTRLR0[ 4: 0] Data Frame Size */
+       cr0 = (transfer->bits_per_word - 1);
+
+       /* CTRLR0[ 7: 6] Frame Format */
+       cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET;
+
+       /*
+        * SPI mode (SCPOL|SCPH)
+        * CTRLR0[ 8] Serial Clock Phase
+        * CTRLR0[ 9] Serial Clock Polarity
+        */
+       cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET;
+       cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET;
+
+       /* CTRLR0[11:10] Transfer Mode */
+       cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET;
+
+       /* CTRLR0[13] Shift Register Loop */
+       cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET;
+
+       return cr0;
 }
+EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a);
 
 static int dw_spi_transfer_one(struct spi_controller *master,
                struct spi_device *spi, struct spi_transfer *transfer)
@@ -313,34 +318,11 @@ static int dw_spi_transfer_one(struct spi_controller *master,
                spi_set_clk(dws, chip->clk_div);
        }
 
+       transfer->effective_speed_hz = dws->max_freq / chip->clk_div;
        dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE);
-       dws->dma_width = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE);
-
-       /* Default SPI mode is SCPOL = 0, SCPH = 0 */
-       cr0 = (transfer->bits_per_word - 1)
-               | (chip->type << SPI_FRF_OFFSET)
-               | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) |
-                       (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) |
-                       (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET))
-               | (chip->tmode << SPI_TMOD_OFFSET);
 
-       /*
-        * Adjust transfer mode if necessary. Requires platform dependent
-        * chipselect mechanism.
-        */
-       if (chip->cs_control) {
-               if (dws->rx && dws->tx)
-                       chip->tmode = SPI_TMOD_TR;
-               else if (dws->rx)
-                       chip->tmode = SPI_TMOD_RO;
-               else
-                       chip->tmode = SPI_TMOD_TO;
-
-               cr0 &= ~SPI_TMOD_MASK;
-               cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
-       }
-
-       dw_writel(dws, DW_SPI_CTRL0, cr0);
+       cr0 = dws->update_cr0(master, spi, transfer);
+       dw_writel(dws, DW_SPI_CTRLR0, cr0);
 
        /* Check if current transfer is a DMA transaction */
        if (master->can_dma && master->can_dma(master, spi, transfer))
@@ -359,9 +341,9 @@ static int dw_spi_transfer_one(struct spi_controller *master,
                        spi_enable_chip(dws, 1);
                        return ret;
                }
-       } else if (!chip->poll_mode) {
+       } else {
                txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes);
-               dw_writel(dws, DW_SPI_TXFLTR, txlevel);
+               dw_writel(dws, DW_SPI_TXFTLR, txlevel);
 
                /* Set the interrupt mask */
                imask |= SPI_INT_TXEI | SPI_INT_TXOI |
@@ -373,14 +355,8 @@ static int dw_spi_transfer_one(struct spi_controller *master,
 
        spi_enable_chip(dws, 1);
 
-       if (dws->dma_mapped) {
-               ret = dws->dma_ops->dma_transfer(dws, transfer);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (chip->poll_mode)
-               return poll_transfer(dws);
+       if (dws->dma_mapped)
+               return dws->dma_ops->dma_transfer(dws, transfer);
 
        return 1;
 }
@@ -399,7 +375,6 @@ static void dw_spi_handle_err(struct spi_controller *master,
 /* This may be called twice for each spi dev */
 static int dw_spi_setup(struct spi_device *spi)
 {
-       struct dw_spi_chip *chip_info = NULL;
        struct chip_data *chip;
 
        /* Only alloc on first setup */
@@ -411,21 +386,6 @@ static int dw_spi_setup(struct spi_device *spi)
                spi_set_ctldata(spi, chip);
        }
 
-       /*
-        * Protocol drivers may change the chip settings, so...
-        * if chip_info exists, use it
-        */
-       chip_info = spi->controller_data;
-
-       /* chip_info doesn't always exist */
-       if (chip_info) {
-               if (chip_info->cs_control)
-                       chip->cs_control = chip_info->cs_control;
-
-               chip->poll_mode = chip_info->poll_mode;
-               chip->type = chip_info->type;
-       }
-
        chip->tmode = SPI_TMOD_TR;
 
        return 0;
@@ -452,11 +412,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
                u32 fifo;
 
                for (fifo = 1; fifo < 256; fifo++) {
-                       dw_writel(dws, DW_SPI_TXFLTR, fifo);
-                       if (fifo != dw_readl(dws, DW_SPI_TXFLTR))
+                       dw_writel(dws, DW_SPI_TXFTLR, fifo);
+                       if (fifo != dw_readl(dws, DW_SPI_TXFTLR))
                                break;
                }
-               dw_writel(dws, DW_SPI_TXFLTR, 0);
+               dw_writel(dws, DW_SPI_TXFTLR, 0);
 
                dws->fifo_len = (fifo == 1) ? 0 : fifo;
                dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
@@ -481,7 +441,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
 
        dws->master = master;
        dws->type = SSI_MOTO_SPI;
-       dws->dma_inited = 0;
        dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
        spin_lock_init(&dws->buf_lock);
 
@@ -517,16 +476,16 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
        spi_hw_init(dev, dws);
 
        if (dws->dma_ops && dws->dma_ops->dma_init) {
-               ret = dws->dma_ops->dma_init(dws);
+               ret = dws->dma_ops->dma_init(dev, dws);
                if (ret) {
                        dev_warn(dev, "DMA init failed\n");
-                       dws->dma_inited = 0;
                } else {
                        master->can_dma = dws->dma_ops->can_dma;
+                       master->flags |= SPI_CONTROLLER_MUST_TX;
                }
        }
 
-       ret = devm_spi_register_controller(dev, master);
+       ret = spi_register_controller(master);
        if (ret) {
                dev_err(&master->dev, "problem registering spi master\n");
                goto err_dma_exit;
@@ -550,6 +509,8 @@ void dw_spi_remove_host(struct dw_spi *dws)
 {
        dw_spi_debugfs_remove(dws);
 
+       spi_unregister_controller(dws->master);
+
        if (dws->dma_ops && dws->dma_ops->dma_exit)
                dws->dma_ops->dma_exit(dws);
 
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
new file mode 100644 (file)
index 0000000..5986c52
--- /dev/null
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Special handling for DW DMA core
+ *
+ * Copyright (c) 2009, 2014 Intel Corporation.
+ */
+
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/irqreturn.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/platform_data/dma-dw.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "spi-dw.h"
+
+#define WAIT_RETRIES   5
+#define RX_BUSY                0
+#define RX_BURST_LEVEL 16
+#define TX_BUSY                1
+#define TX_BURST_LEVEL 16
+
+static bool dw_spi_dma_chan_filter(struct dma_chan *chan, void *param)
+{
+       struct dw_dma_slave *s = param;
+
+       if (s->dma_dev != chan->device->dev)
+               return false;
+
+       chan->private = s;
+       return true;
+}
+
+static void dw_spi_dma_maxburst_init(struct dw_spi *dws)
+{
+       struct dma_slave_caps caps;
+       u32 max_burst, def_burst;
+       int ret;
+
+       def_burst = dws->fifo_len / 2;
+
+       ret = dma_get_slave_caps(dws->rxchan, &caps);
+       if (!ret && caps.max_burst)
+               max_burst = caps.max_burst;
+       else
+               max_burst = RX_BURST_LEVEL;
+
+       dws->rxburst = min(max_burst, def_burst);
+
+       ret = dma_get_slave_caps(dws->txchan, &caps);
+       if (!ret && caps.max_burst)
+               max_burst = caps.max_burst;
+       else
+               max_burst = TX_BURST_LEVEL;
+
+       dws->txburst = min(max_burst, def_burst);
+}
+
+static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
+{
+       struct dw_dma_slave dma_tx = { .dst_id = 1 }, *tx = &dma_tx;
+       struct dw_dma_slave dma_rx = { .src_id = 0 }, *rx = &dma_rx;
+       struct pci_dev *dma_dev;
+       dma_cap_mask_t mask;
+
+       /*
+        * Get pci device for DMA controller, currently it could only
+        * be the DMA controller of Medfield
+        */
+       dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
+       if (!dma_dev)
+               return -ENODEV;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       /* 1. Init rx channel */
+       rx->dma_dev = &dma_dev->dev;
+       dws->rxchan = dma_request_channel(mask, dw_spi_dma_chan_filter, rx);
+       if (!dws->rxchan)
+               goto err_exit;
+
+       /* 2. Init tx channel */
+       tx->dma_dev = &dma_dev->dev;
+       dws->txchan = dma_request_channel(mask, dw_spi_dma_chan_filter, tx);
+       if (!dws->txchan)
+               goto free_rxchan;
+
+       dws->master->dma_rx = dws->rxchan;
+       dws->master->dma_tx = dws->txchan;
+
+       init_completion(&dws->dma_completion);
+
+       dw_spi_dma_maxburst_init(dws);
+
+       return 0;
+
+free_rxchan:
+       dma_release_channel(dws->rxchan);
+       dws->rxchan = NULL;
+err_exit:
+       return -EBUSY;
+}
+
+static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
+{
+       dws->rxchan = dma_request_slave_channel(dev, "rx");
+       if (!dws->rxchan)
+               return -ENODEV;
+
+       dws->txchan = dma_request_slave_channel(dev, "tx");
+       if (!dws->txchan) {
+               dma_release_channel(dws->rxchan);
+               dws->rxchan = NULL;
+               return -ENODEV;
+       }
+
+       dws->master->dma_rx = dws->rxchan;
+       dws->master->dma_tx = dws->txchan;
+
+       init_completion(&dws->dma_completion);
+
+       dw_spi_dma_maxburst_init(dws);
+
+       return 0;
+}
+
+static void dw_spi_dma_exit(struct dw_spi *dws)
+{
+       if (dws->txchan) {
+               dmaengine_terminate_sync(dws->txchan);
+               dma_release_channel(dws->txchan);
+       }
+
+       if (dws->rxchan) {
+               dmaengine_terminate_sync(dws->rxchan);
+               dma_release_channel(dws->rxchan);
+       }
+
+       dw_writel(dws, DW_SPI_DMACR, 0);
+}
+
+static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws)
+{
+       u16 irq_status = dw_readl(dws, DW_SPI_ISR);
+
+       if (!irq_status)
+               return IRQ_NONE;
+
+       dw_readl(dws, DW_SPI_ICR);
+       spi_reset_chip(dws);
+
+       dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
+       dws->master->cur_msg->status = -EIO;
+       complete(&dws->dma_completion);
+       return IRQ_HANDLED;
+}
+
+static bool dw_spi_can_dma(struct spi_controller *master,
+                          struct spi_device *spi, struct spi_transfer *xfer)
+{
+       struct dw_spi *dws = spi_controller_get_devdata(master);
+
+       return xfer->len > dws->fifo_len;
+}
+
+static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes)
+{
+       if (n_bytes == 1)
+               return DMA_SLAVE_BUSWIDTH_1_BYTE;
+       else if (n_bytes == 2)
+               return DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+       return DMA_SLAVE_BUSWIDTH_UNDEFINED;
+}
+
+static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer)
+{
+       unsigned long long ms;
+
+       ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE;
+       do_div(ms, xfer->effective_speed_hz);
+       ms += ms + 200;
+
+       if (ms > UINT_MAX)
+               ms = UINT_MAX;
+
+       ms = wait_for_completion_timeout(&dws->dma_completion,
+                                        msecs_to_jiffies(ms));
+
+       if (ms == 0) {
+               dev_err(&dws->master->cur_msg->spi->dev,
+                       "DMA transaction timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws)
+{
+       return !(dw_readl(dws, DW_SPI_SR) & SR_TF_EMPT);
+}
+
+static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
+                                  struct spi_transfer *xfer)
+{
+       int retry = WAIT_RETRIES;
+       struct spi_delay delay;
+       u32 nents;
+
+       nents = dw_readl(dws, DW_SPI_TXFLR);
+       delay.unit = SPI_DELAY_UNIT_SCK;
+       delay.value = nents * dws->n_bytes * BITS_PER_BYTE;
+
+       while (dw_spi_dma_tx_busy(dws) && retry--)
+               spi_delay_exec(&delay, xfer);
+
+       if (retry < 0) {
+               dev_err(&dws->master->dev, "Tx hanged up\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/*
+ * dws->dma_chan_busy is set before the dma transfer starts, callback for tx
+ * channel will clear a corresponding bit.
+ */
+static void dw_spi_dma_tx_done(void *arg)
+{
+       struct dw_spi *dws = arg;
+
+       clear_bit(TX_BUSY, &dws->dma_chan_busy);
+       if (test_bit(RX_BUSY, &dws->dma_chan_busy))
+               return;
+
+       dw_writel(dws, DW_SPI_DMACR, 0);
+       complete(&dws->dma_completion);
+}
+
+static struct dma_async_tx_descriptor *
+dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer)
+{
+       struct dma_slave_config txconf;
+       struct dma_async_tx_descriptor *txdesc;
+
+       if (!xfer->tx_buf)
+               return NULL;
+
+       memset(&txconf, 0, sizeof(txconf));
+       txconf.direction = DMA_MEM_TO_DEV;
+       txconf.dst_addr = dws->dma_addr;
+       txconf.dst_maxburst = dws->txburst;
+       txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+       txconf.device_fc = false;
+
+       dmaengine_slave_config(dws->txchan, &txconf);
+
+       txdesc = dmaengine_prep_slave_sg(dws->txchan,
+                               xfer->tx_sg.sgl,
+                               xfer->tx_sg.nents,
+                               DMA_MEM_TO_DEV,
+                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!txdesc)
+               return NULL;
+
+       txdesc->callback = dw_spi_dma_tx_done;
+       txdesc->callback_param = dws;
+
+       return txdesc;
+}
+
+static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws)
+{
+       return !!(dw_readl(dws, DW_SPI_SR) & SR_RF_NOT_EMPT);
+}
+
+static int dw_spi_dma_wait_rx_done(struct dw_spi *dws)
+{
+       int retry = WAIT_RETRIES;
+       struct spi_delay delay;
+       unsigned long ns, us;
+       u32 nents;
+
+       /*
+        * It's unlikely that DMA engine is still doing the data fetching, but
+        * if it's let's give it some reasonable time. The timeout calculation
+        * is based on the synchronous APB/SSI reference clock rate, on a
+        * number of data entries left in the Rx FIFO, times a number of clock
+        * periods normally needed for a single APB read/write transaction
+        * without PREADY signal utilized (which is true for the DW APB SSI
+        * controller).
+        */
+       nents = dw_readl(dws, DW_SPI_RXFLR);
+       ns = 4U * NSEC_PER_SEC / dws->max_freq * nents;
+       if (ns <= NSEC_PER_USEC) {
+               delay.unit = SPI_DELAY_UNIT_NSECS;
+               delay.value = ns;
+       } else {
+               us = DIV_ROUND_UP(ns, NSEC_PER_USEC);
+               delay.unit = SPI_DELAY_UNIT_USECS;
+               delay.value = clamp_val(us, 0, USHRT_MAX);
+       }
+
+       while (dw_spi_dma_rx_busy(dws) && retry--)
+               spi_delay_exec(&delay, NULL);
+
+       if (retry < 0) {
+               dev_err(&dws->master->dev, "Rx hanged up\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/*
+ * dws->dma_chan_busy is set before the dma transfer starts, callback for rx
+ * channel will clear a corresponding bit.
+ */
+static void dw_spi_dma_rx_done(void *arg)
+{
+       struct dw_spi *dws = arg;
+
+       clear_bit(RX_BUSY, &dws->dma_chan_busy);
+       if (test_bit(TX_BUSY, &dws->dma_chan_busy))
+               return;
+
+       dw_writel(dws, DW_SPI_DMACR, 0);
+       complete(&dws->dma_completion);
+}
+
+static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws,
+               struct spi_transfer *xfer)
+{
+       struct dma_slave_config rxconf;
+       struct dma_async_tx_descriptor *rxdesc;
+
+       if (!xfer->rx_buf)
+               return NULL;
+
+       memset(&rxconf, 0, sizeof(rxconf));
+       rxconf.direction = DMA_DEV_TO_MEM;
+       rxconf.src_addr = dws->dma_addr;
+       rxconf.src_maxburst = dws->rxburst;
+       rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes);
+       rxconf.device_fc = false;
+
+       dmaengine_slave_config(dws->rxchan, &rxconf);
+
+       rxdesc = dmaengine_prep_slave_sg(dws->rxchan,
+                               xfer->rx_sg.sgl,
+                               xfer->rx_sg.nents,
+                               DMA_DEV_TO_MEM,
+                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+       if (!rxdesc)
+               return NULL;
+
+       rxdesc->callback = dw_spi_dma_rx_done;
+       rxdesc->callback_param = dws;
+
+       return rxdesc;
+}
+
+static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
+{
+       u16 imr = 0, dma_ctrl = 0;
+
+       dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1);
+       dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst);
+
+       if (xfer->tx_buf)
+               dma_ctrl |= SPI_DMA_TDMAE;
+       if (xfer->rx_buf)
+               dma_ctrl |= SPI_DMA_RDMAE;
+       dw_writel(dws, DW_SPI_DMACR, dma_ctrl);
+
+       /* Set the interrupt mask */
+       if (xfer->tx_buf)
+               imr |= SPI_INT_TXOI;
+       if (xfer->rx_buf)
+               imr |= SPI_INT_RXUI | SPI_INT_RXOI;
+       spi_umask_intr(dws, imr);
+
+       reinit_completion(&dws->dma_completion);
+
+       dws->transfer_handler = dw_spi_dma_transfer_handler;
+
+       return 0;
+}
+
+static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
+{
+       struct dma_async_tx_descriptor *txdesc, *rxdesc;
+       int ret;
+
+       /* Prepare the TX dma transfer */
+       txdesc = dw_spi_dma_prepare_tx(dws, xfer);
+
+       /* Prepare the RX dma transfer */
+       rxdesc = dw_spi_dma_prepare_rx(dws, xfer);
+
+       /* rx must be started before tx due to spi instinct */
+       if (rxdesc) {
+               set_bit(RX_BUSY, &dws->dma_chan_busy);
+               dmaengine_submit(rxdesc);
+               dma_async_issue_pending(dws->rxchan);
+       }
+
+       if (txdesc) {
+               set_bit(TX_BUSY, &dws->dma_chan_busy);
+               dmaengine_submit(txdesc);
+               dma_async_issue_pending(dws->txchan);
+       }
+
+       ret = dw_spi_dma_wait(dws, xfer);
+       if (ret)
+               return ret;
+
+       if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) {
+               ret = dw_spi_dma_wait_tx_done(dws, xfer);
+               if (ret)
+                       return ret;
+       }
+
+       if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS)
+               ret = dw_spi_dma_wait_rx_done(dws);
+
+       return ret;
+}
+
+static void dw_spi_dma_stop(struct dw_spi *dws)
+{
+       if (test_bit(TX_BUSY, &dws->dma_chan_busy)) {
+               dmaengine_terminate_sync(dws->txchan);
+               clear_bit(TX_BUSY, &dws->dma_chan_busy);
+       }
+       if (test_bit(RX_BUSY, &dws->dma_chan_busy)) {
+               dmaengine_terminate_sync(dws->rxchan);
+               clear_bit(RX_BUSY, &dws->dma_chan_busy);
+       }
+
+       dw_writel(dws, DW_SPI_DMACR, 0);
+}
+
+static const struct dw_spi_dma_ops dw_spi_dma_mfld_ops = {
+       .dma_init       = dw_spi_dma_init_mfld,
+       .dma_exit       = dw_spi_dma_exit,
+       .dma_setup      = dw_spi_dma_setup,
+       .can_dma        = dw_spi_can_dma,
+       .dma_transfer   = dw_spi_dma_transfer,
+       .dma_stop       = dw_spi_dma_stop,
+};
+
+void dw_spi_dma_setup_mfld(struct dw_spi *dws)
+{
+       dws->dma_ops = &dw_spi_dma_mfld_ops;
+}
+EXPORT_SYMBOL_GPL(dw_spi_dma_setup_mfld);
+
+static const struct dw_spi_dma_ops dw_spi_dma_generic_ops = {
+       .dma_init       = dw_spi_dma_init_generic,
+       .dma_exit       = dw_spi_dma_exit,
+       .dma_setup      = dw_spi_dma_setup,
+       .can_dma        = dw_spi_can_dma,
+       .dma_transfer   = dw_spi_dma_transfer,
+       .dma_stop       = dw_spi_dma_stop,
+};
+
+void dw_spi_dma_setup_generic(struct dw_spi *dws)
+{
+       dws->dma_ops = &dw_spi_dma_generic_ops;
+}
+EXPORT_SYMBOL_GPL(dw_spi_dma_setup_generic);
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
deleted file mode 100644 (file)
index 0d86c37..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Special handling for DW core on Intel MID platform
- *
- * Copyright (c) 2009, 2014 Intel Corporation.
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/types.h>
-
-#include "spi-dw.h"
-
-#ifdef CONFIG_SPI_DW_MID_DMA
-#include <linux/pci.h>
-#include <linux/platform_data/dma-dw.h>
-
-#define RX_BUSY                0
-#define TX_BUSY                1
-
-static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 };
-static struct dw_dma_slave mid_dma_rx = { .src_id = 0 };
-
-static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
-{
-       struct dw_dma_slave *s = param;
-
-       if (s->dma_dev != chan->device->dev)
-               return false;
-
-       chan->private = s;
-       return true;
-}
-
-static int mid_spi_dma_init(struct dw_spi *dws)
-{
-       struct pci_dev *dma_dev;
-       struct dw_dma_slave *tx = dws->dma_tx;
-       struct dw_dma_slave *rx = dws->dma_rx;
-       dma_cap_mask_t mask;
-
-       /*
-        * Get pci device for DMA controller, currently it could only
-        * be the DMA controller of Medfield
-        */
-       dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
-       if (!dma_dev)
-               return -ENODEV;
-
-       dma_cap_zero(mask);
-       dma_cap_set(DMA_SLAVE, mask);
-
-       /* 1. Init rx channel */
-       rx->dma_dev = &dma_dev->dev;
-       dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx);
-       if (!dws->rxchan)
-               goto err_exit;
-       dws->master->dma_rx = dws->rxchan;
-
-       /* 2. Init tx channel */
-       tx->dma_dev = &dma_dev->dev;
-       dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx);
-       if (!dws->txchan)
-               goto free_rxchan;
-       dws->master->dma_tx = dws->txchan;
-
-       dws->dma_inited = 1;
-       return 0;
-
-free_rxchan:
-       dma_release_channel(dws->rxchan);
-err_exit:
-       return -EBUSY;
-}
-
-static void mid_spi_dma_exit(struct dw_spi *dws)
-{
-       if (!dws->dma_inited)
-               return;
-
-       dmaengine_terminate_sync(dws->txchan);
-       dma_release_channel(dws->txchan);
-
-       dmaengine_terminate_sync(dws->rxchan);
-       dma_release_channel(dws->rxchan);
-}
-
-static irqreturn_t dma_transfer(struct dw_spi *dws)
-{
-       u16 irq_status = dw_readl(dws, DW_SPI_ISR);
-
-       if (!irq_status)
-               return IRQ_NONE;
-
-       dw_readl(dws, DW_SPI_ICR);
-       spi_reset_chip(dws);
-
-       dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__);
-       dws->master->cur_msg->status = -EIO;
-       spi_finalize_current_transfer(dws->master);
-       return IRQ_HANDLED;
-}
-
-static bool mid_spi_can_dma(struct spi_controller *master,
-               struct spi_device *spi, struct spi_transfer *xfer)
-{
-       struct dw_spi *dws = spi_controller_get_devdata(master);
-
-       if (!dws->dma_inited)
-               return false;
-
-       return xfer->len > dws->fifo_len;
-}
-
-static enum dma_slave_buswidth convert_dma_width(u32 dma_width) {
-       if (dma_width == 1)
-               return DMA_SLAVE_BUSWIDTH_1_BYTE;
-       else if (dma_width == 2)
-               return DMA_SLAVE_BUSWIDTH_2_BYTES;
-
-       return DMA_SLAVE_BUSWIDTH_UNDEFINED;
-}
-
-/*
- * dws->dma_chan_busy is set before the dma transfer starts, callback for tx
- * channel will clear a corresponding bit.
- */
-static void dw_spi_dma_tx_done(void *arg)
-{
-       struct dw_spi *dws = arg;
-
-       clear_bit(TX_BUSY, &dws->dma_chan_busy);
-       if (test_bit(RX_BUSY, &dws->dma_chan_busy))
-               return;
-       spi_finalize_current_transfer(dws->master);
-}
-
-static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws,
-               struct spi_transfer *xfer)
-{
-       struct dma_slave_config txconf;
-       struct dma_async_tx_descriptor *txdesc;
-
-       if (!xfer->tx_buf)
-               return NULL;
-
-       txconf.direction = DMA_MEM_TO_DEV;
-       txconf.dst_addr = dws->dma_addr;
-       txconf.dst_maxburst = 16;
-       txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-       txconf.dst_addr_width = convert_dma_width(dws->dma_width);
-       txconf.device_fc = false;
-
-       dmaengine_slave_config(dws->txchan, &txconf);
-
-       txdesc = dmaengine_prep_slave_sg(dws->txchan,
-                               xfer->tx_sg.sgl,
-                               xfer->tx_sg.nents,
-                               DMA_MEM_TO_DEV,
-                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-       if (!txdesc)
-               return NULL;
-
-       txdesc->callback = dw_spi_dma_tx_done;
-       txdesc->callback_param = dws;
-
-       return txdesc;
-}
-
-/*
- * dws->dma_chan_busy is set before the dma transfer starts, callback for rx
- * channel will clear a corresponding bit.
- */
-static void dw_spi_dma_rx_done(void *arg)
-{
-       struct dw_spi *dws = arg;
-
-       clear_bit(RX_BUSY, &dws->dma_chan_busy);
-       if (test_bit(TX_BUSY, &dws->dma_chan_busy))
-               return;
-       spi_finalize_current_transfer(dws->master);
-}
-
-static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws,
-               struct spi_transfer *xfer)
-{
-       struct dma_slave_config rxconf;
-       struct dma_async_tx_descriptor *rxdesc;
-
-       if (!xfer->rx_buf)
-               return NULL;
-
-       rxconf.direction = DMA_DEV_TO_MEM;
-       rxconf.src_addr = dws->dma_addr;
-       rxconf.src_maxburst = 16;
-       rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-       rxconf.src_addr_width = convert_dma_width(dws->dma_width);
-       rxconf.device_fc = false;
-
-       dmaengine_slave_config(dws->rxchan, &rxconf);
-
-       rxdesc = dmaengine_prep_slave_sg(dws->rxchan,
-                               xfer->rx_sg.sgl,
-                               xfer->rx_sg.nents,
-                               DMA_DEV_TO_MEM,
-                               DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-       if (!rxdesc)
-               return NULL;
-
-       rxdesc->callback = dw_spi_dma_rx_done;
-       rxdesc->callback_param = dws;
-
-       return rxdesc;
-}
-
-static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
-{
-       u16 dma_ctrl = 0;
-
-       dw_writel(dws, DW_SPI_DMARDLR, 0xf);
-       dw_writel(dws, DW_SPI_DMATDLR, 0x10);
-
-       if (xfer->tx_buf)
-               dma_ctrl |= SPI_DMA_TDMAE;
-       if (xfer->rx_buf)
-               dma_ctrl |= SPI_DMA_RDMAE;
-       dw_writel(dws, DW_SPI_DMACR, dma_ctrl);
-
-       /* Set the interrupt mask */
-       spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI);
-
-       dws->transfer_handler = dma_transfer;
-
-       return 0;
-}
-
-static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
-{
-       struct dma_async_tx_descriptor *txdesc, *rxdesc;
-
-       /* Prepare the TX dma transfer */
-       txdesc = dw_spi_dma_prepare_tx(dws, xfer);
-
-       /* Prepare the RX dma transfer */
-       rxdesc = dw_spi_dma_prepare_rx(dws, xfer);
-
-       /* rx must be started before tx due to spi instinct */
-       if (rxdesc) {
-               set_bit(RX_BUSY, &dws->dma_chan_busy);
-               dmaengine_submit(rxdesc);
-               dma_async_issue_pending(dws->rxchan);
-       }
-
-       if (txdesc) {
-               set_bit(TX_BUSY, &dws->dma_chan_busy);
-               dmaengine_submit(txdesc);
-               dma_async_issue_pending(dws->txchan);
-       }
-
-       return 0;
-}
-
-static void mid_spi_dma_stop(struct dw_spi *dws)
-{
-       if (test_bit(TX_BUSY, &dws->dma_chan_busy)) {
-               dmaengine_terminate_sync(dws->txchan);
-               clear_bit(TX_BUSY, &dws->dma_chan_busy);
-       }
-       if (test_bit(RX_BUSY, &dws->dma_chan_busy)) {
-               dmaengine_terminate_sync(dws->rxchan);
-               clear_bit(RX_BUSY, &dws->dma_chan_busy);
-       }
-}
-
-static const struct dw_spi_dma_ops mid_dma_ops = {
-       .dma_init       = mid_spi_dma_init,
-       .dma_exit       = mid_spi_dma_exit,
-       .dma_setup      = mid_spi_dma_setup,
-       .can_dma        = mid_spi_can_dma,
-       .dma_transfer   = mid_spi_dma_transfer,
-       .dma_stop       = mid_spi_dma_stop,
-};
-#endif
-
-/* Some specific info for SPI0 controller on Intel MID */
-
-/* HW info for MRST Clk Control Unit, 32b reg per controller */
-#define MRST_SPI_CLK_BASE      100000000       /* 100m */
-#define MRST_CLK_SPI_REG       0xff11d86c
-#define CLK_SPI_BDIV_OFFSET    0
-#define CLK_SPI_BDIV_MASK      0x00000007
-#define CLK_SPI_CDIV_OFFSET    9
-#define CLK_SPI_CDIV_MASK      0x00000e00
-#define CLK_SPI_DISABLE_OFFSET 8
-
-int dw_spi_mid_init(struct dw_spi *dws)
-{
-       void __iomem *clk_reg;
-       u32 clk_cdiv;
-
-       clk_reg = ioremap(MRST_CLK_SPI_REG, 16);
-       if (!clk_reg)
-               return -ENOMEM;
-
-       /* Get SPI controller operating freq info */
-       clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32));
-       clk_cdiv &= CLK_SPI_CDIV_MASK;
-       clk_cdiv >>= CLK_SPI_CDIV_OFFSET;
-       dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
-
-       iounmap(clk_reg);
-
-#ifdef CONFIG_SPI_DW_MID_DMA
-       dws->dma_tx = &mid_dma_tx;
-       dws->dma_rx = &mid_dma_rx;
-       dws->dma_ops = &mid_dma_ops;
-#endif
-       return 0;
-}
index 384a3ab6dc2d0d2ebfd7a1c8723898847ba4fe17..403403deae6640fb409d31f2322da594cb7f86c5 100644 (file)
@@ -7,7 +7,6 @@
 
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
@@ -20,6 +19,7 @@
 #include <linux/acpi.h>
 #include <linux/property.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 
 #include "spi-dw.h"
 
@@ -30,6 +30,7 @@ struct dw_spi_mmio {
        struct clk     *clk;
        struct clk     *pclk;
        void           *priv;
+       struct reset_control *rstc;
 };
 
 #define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL      0x24
@@ -44,6 +45,13 @@ struct dw_spi_mmio {
 #define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE  BIT(13)
 #define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x)      (x << 5)
 
+/*
+ * For Keem Bay, CTRLR0[31] is used to select controller mode.
+ * 0: SSI is slave
+ * 1: SSI is master
+ */
+#define KEEMBAY_CTRLR0_SSIC_IS_MST             BIT(31)
+
 struct dw_spi_mscc {
        struct regmap       *syscon;
        void __iomem        *spi_mst;
@@ -106,6 +114,9 @@ static int dw_spi_mscc_init(struct platform_device *pdev,
        dwsmmio->dws.set_cs = dw_spi_mscc_set_cs;
        dwsmmio->priv = dwsmscc;
 
+       /* Register hook to configure CTRLR0 */
+       dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
+
        return 0;
 }
 
@@ -128,6 +139,49 @@ static int dw_spi_alpine_init(struct platform_device *pdev,
 {
        dwsmmio->dws.cs_override = 1;
 
+       /* Register hook to configure CTRLR0 */
+       dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
+
+       return 0;
+}
+
+static int dw_spi_dw_apb_init(struct platform_device *pdev,
+                             struct dw_spi_mmio *dwsmmio)
+{
+       /* Register hook to configure CTRLR0 */
+       dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
+
+       dw_spi_dma_setup_generic(&dwsmmio->dws);
+
+       return 0;
+}
+
+static int dw_spi_dwc_ssi_init(struct platform_device *pdev,
+                              struct dw_spi_mmio *dwsmmio)
+{
+       /* Register hook to configure CTRLR0 */
+       dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a;
+
+       dw_spi_dma_setup_generic(&dwsmmio->dws);
+
+       return 0;
+}
+
+static u32 dw_spi_update_cr0_keembay(struct spi_controller *master,
+                                    struct spi_device *spi,
+                                    struct spi_transfer *transfer)
+{
+       u32 cr0 = dw_spi_update_cr0_v1_01a(master, spi, transfer);
+
+       return cr0 | KEEMBAY_CTRLR0_SSIC_IS_MST;
+}
+
+static int dw_spi_keembay_init(struct platform_device *pdev,
+                              struct dw_spi_mmio *dwsmmio)
+{
+       /* Register hook to configure CTRLR0 */
+       dwsmmio->dws.update_cr0 = dw_spi_update_cr0_keembay;
+
        return 0;
 }
 
@@ -136,6 +190,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
        int (*init_func)(struct platform_device *pdev,
                         struct dw_spi_mmio *dwsmmio);
        struct dw_spi_mmio *dwsmmio;
+       struct resource *mem;
        struct dw_spi *dws;
        int ret;
        int num_cs;
@@ -148,11 +203,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
        dws = &dwsmmio->dws;
 
        /* Get basic io resource and map it */
-       dws->regs = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(dws->regs)) {
-               dev_err(&pdev->dev, "SPI region map failed\n");
+       dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+       if (IS_ERR(dws->regs))
                return PTR_ERR(dws->regs);
-       }
+
+       dws->paddr = mem->start;
 
        dws->irq = platform_get_irq(pdev, 0);
        if (dws->irq < 0)
@@ -175,6 +230,14 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
        if (ret)
                goto out_clk;
 
+       /* find an optional reset controller */
+       dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
+       if (IS_ERR(dwsmmio->rstc)) {
+               ret = PTR_ERR(dwsmmio->rstc);
+               goto out_clk;
+       }
+       reset_control_deassert(dwsmmio->rstc);
+
        dws->bus_num = pdev->id;
 
        dws->max_freq = clk_get_rate(dwsmmio->clk);
@@ -208,6 +271,8 @@ out:
        clk_disable_unprepare(dwsmmio->pclk);
 out_clk:
        clk_disable_unprepare(dwsmmio->clk);
+       reset_control_assert(dwsmmio->rstc);
+
        return ret;
 }
 
@@ -219,25 +284,30 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        clk_disable_unprepare(dwsmmio->pclk);
        clk_disable_unprepare(dwsmmio->clk);
+       reset_control_assert(dwsmmio->rstc);
 
        return 0;
 }
 
 static const struct of_device_id dw_spi_mmio_of_match[] = {
-       { .compatible = "snps,dw-apb-ssi", },
+       { .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init},
        { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init},
        { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init},
        { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init},
-       { .compatible = "renesas,rzn1-spi", },
+       { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init},
+       { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init},
+       { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init},
        { /* end of table */}
 };
 MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
 
+#ifdef CONFIG_ACPI
 static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
-       {"HISI0173", 0},
+       {"HISI0173", (kernel_ulong_t)dw_spi_dw_apb_init},
        {},
 };
 MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
+#endif
 
 static struct platform_driver dw_spi_mmio_driver = {
        .probe          = dw_spi_mmio_probe,
index 12c131b5fb4ec602b737ed7f002fa0c081578914..2ea73809ca345bb5e11e794126f90b46aa9ceb77 100644 (file)
@@ -5,7 +5,6 @@
  * Copyright (c) 2009, 2014 Intel Corporation.
  */
 
-#include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define DRIVER_NAME "dw_spi_pci"
 
+/* HW info for MRST Clk Control Unit, 32b reg per controller */
+#define MRST_SPI_CLK_BASE      100000000       /* 100m */
+#define MRST_CLK_SPI_REG       0xff11d86c
+#define CLK_SPI_BDIV_OFFSET    0
+#define CLK_SPI_BDIV_MASK      0x00000007
+#define CLK_SPI_CDIV_OFFSET    9
+#define CLK_SPI_CDIV_MASK      0x00000e00
+#define CLK_SPI_DISABLE_OFFSET 8
+
 struct spi_pci_desc {
        int     (*setup)(struct dw_spi *);
        u16     num_cs;
@@ -23,19 +31,55 @@ struct spi_pci_desc {
        u32     max_freq;
 };
 
+static int spi_mid_init(struct dw_spi *dws)
+{
+       void __iomem *clk_reg;
+       u32 clk_cdiv;
+
+       clk_reg = ioremap(MRST_CLK_SPI_REG, 16);
+       if (!clk_reg)
+               return -ENOMEM;
+
+       /* Get SPI controller operating freq info */
+       clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32));
+       clk_cdiv &= CLK_SPI_CDIV_MASK;
+       clk_cdiv >>= CLK_SPI_CDIV_OFFSET;
+       dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
+
+       iounmap(clk_reg);
+
+       /* Register hook to configure CTRLR0 */
+       dws->update_cr0 = dw_spi_update_cr0;
+
+       dw_spi_dma_setup_mfld(dws);
+
+       return 0;
+}
+
+static int spi_generic_init(struct dw_spi *dws)
+{
+       /* Register hook to configure CTRLR0 */
+       dws->update_cr0 = dw_spi_update_cr0;
+
+       dw_spi_dma_setup_generic(dws);
+
+       return 0;
+}
+
 static struct spi_pci_desc spi_pci_mid_desc_1 = {
-       .setup = dw_spi_mid_init,
+       .setup = spi_mid_init,
        .num_cs = 5,
        .bus_num = 0,
 };
 
 static struct spi_pci_desc spi_pci_mid_desc_2 = {
-       .setup = dw_spi_mid_init,
+       .setup = spi_mid_init,
        .num_cs = 2,
        .bus_num = 1,
 };
 
 static struct spi_pci_desc spi_pci_ehl_desc = {
+       .setup = spi_generic_init,
        .num_cs = 2,
        .bus_num = -1,
        .max_freq = 100000000,
index 1bf5713e047d35c6f44fd329860fa0db490a3e61..151ba316619e6bf31b71936c3015841f232fa2f8 100644 (file)
@@ -2,18 +2,21 @@
 #ifndef DW_SPI_HEADER_H
 #define DW_SPI_HEADER_H
 
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/irqreturn.h>
 #include <linux/io.h>
 #include <linux/scatterlist.h>
 
 /* Register offsets */
-#define DW_SPI_CTRL                  0x00
-#define DW_SPI_CTRL                  0x04
+#define DW_SPI_CTRLR0                  0x00
+#define DW_SPI_CTRLR1                  0x04
 #define DW_SPI_SSIENR                  0x08
 #define DW_SPI_MWCR                    0x0c
 #define DW_SPI_SER                     0x10
 #define DW_SPI_BAUDR                   0x14
-#define DW_SPI_TXFLTR                  0x18
-#define DW_SPI_RXFLTR                  0x1c
+#define DW_SPI_TXFTLR                  0x18
+#define DW_SPI_RXFTLR                  0x1c
 #define DW_SPI_TXFLR                   0x20
 #define DW_SPI_RXFLR                   0x24
 #define DW_SPI_SR                      0x28
 #define SPI_SRL_OFFSET                 11
 #define SPI_CFS_OFFSET                 12
 
+/* Bit fields in CTRLR0 based on DWC_ssi_databook.pdf v1.01a */
+#define DWC_SSI_CTRLR0_SRL_OFFSET      13
+#define DWC_SSI_CTRLR0_TMOD_OFFSET     10
+#define DWC_SSI_CTRLR0_TMOD_MASK       GENMASK(11, 10)
+#define DWC_SSI_CTRLR0_SCPOL_OFFSET    9
+#define DWC_SSI_CTRLR0_SCPH_OFFSET     8
+#define DWC_SSI_CTRLR0_FRF_OFFSET      6
+#define DWC_SSI_CTRLR0_DFS_OFFSET      0
+
 /* Bit fields in SR, 7 bits */
 #define SR_MASK                                0x7f            /* cover 7 bits */
 #define SR_BUSY                                (1 << 0)
@@ -90,7 +102,7 @@ enum dw_ssi_type {
 
 struct dw_spi;
 struct dw_spi_dma_ops {
-       int (*dma_init)(struct dw_spi *dws);
+       int (*dma_init)(struct device *dev, struct dw_spi *dws);
        void (*dma_exit)(struct dw_spi *dws);
        int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
        bool (*can_dma)(struct spi_controller *master, struct spi_device *spi,
@@ -114,6 +126,8 @@ struct dw_spi {
        u16                     bus_num;
        u16                     num_cs;         /* supported slave numbers */
        void (*set_cs)(struct spi_device *spi, bool enable);
+       u32 (*update_cr0)(struct spi_controller *master, struct spi_device *spi,
+                         struct spi_transfer *transfer);
 
        /* Current message transfer state info */
        size_t                  len;
@@ -124,24 +138,22 @@ struct dw_spi {
        void                    *rx_end;
        int                     dma_mapped;
        u8                      n_bytes;        /* current is a 1/2 bytes op */
-       u32                     dma_width;
        irqreturn_t             (*transfer_handler)(struct dw_spi *dws);
        u32                     current_freq;   /* frequency in hz */
 
        /* DMA info */
-       int                     dma_inited;
        struct dma_chan         *txchan;
+       u32                     txburst;
        struct dma_chan         *rxchan;
+       u32                     rxburst;
        unsigned long           dma_chan_busy;
        dma_addr_t              dma_addr; /* phy address of the Data register */
        const struct dw_spi_dma_ops *dma_ops;
-       void                    *dma_tx;
-       void                    *dma_rx;
+       struct completion       dma_completion;
 
-       /* Bus interface info */
-       void                    *priv;
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
+       struct debugfs_regset32 regset;
 #endif
 };
 
@@ -235,24 +247,28 @@ static inline void spi_shutdown_chip(struct dw_spi *dws)
        spi_set_clk(dws, 0);
 }
 
-/*
- * Each SPI slave device to work with dw_api controller should
- * has such a structure claiming its working mode (poll or PIO/DMA),
- * which can be save in the "controller_data" member of the
- * struct spi_device.
- */
-struct dw_spi_chip {
-       u8 poll_mode;   /* 1 for controller polling mode */
-       u8 type;        /* SPI/SSP/MicroWire */
-       void (*cs_control)(u32 command);
-};
-
 extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
 extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
 extern void dw_spi_remove_host(struct dw_spi *dws);
 extern int dw_spi_suspend_host(struct dw_spi *dws);
 extern int dw_spi_resume_host(struct dw_spi *dws);
+extern u32 dw_spi_update_cr0(struct spi_controller *master,
+                            struct spi_device *spi,
+                            struct spi_transfer *transfer);
+extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master,
+                                   struct spi_device *spi,
+                                   struct spi_transfer *transfer);
+
+#ifdef CONFIG_SPI_DW_DMA
+
+extern void dw_spi_dma_setup_mfld(struct dw_spi *dws);
+extern void dw_spi_dma_setup_generic(struct dw_spi *dws);
+
+#else
+
+static inline void dw_spi_dma_setup_mfld(struct dw_spi *dws) {}
+static inline void dw_spi_dma_setup_generic(struct dw_spi *dws) {}
+
+#endif /* !CONFIG_SPI_DW_DMA */
 
-/* platform related setup */
-extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */
 #endif /* DW_SPI_HEADER_H */
index 4e1ccd4e52b6c1644a66cae83eb705f6c37a3031..8c854b187b1d0535e8ca7b4100f79fa1897e860f 100644 (file)
@@ -31,7 +31,8 @@
 #include <linux/platform_data/spi-ep93xx.h>
 
 #define SSPCR0                 0x0000
-#define SSPCR0_MODE_SHIFT      6
+#define SSPCR0_SPO             BIT(6)
+#define SSPCR0_SPH             BIT(7)
 #define SSPCR0_SCR_SHIFT       8
 
 #define SSPCR1                 0x0004
@@ -159,7 +160,10 @@ static int ep93xx_spi_chip_setup(struct spi_master *master,
                return err;
 
        cr0 = div_scr << SSPCR0_SCR_SHIFT;
-       cr0 |= (spi->mode & (SPI_CPHA | SPI_CPOL)) << SSPCR0_MODE_SHIFT;
+       if (spi->mode & SPI_CPOL)
+               cr0 |= SSPCR0_SPO;
+       if (spi->mode & SPI_CPHA)
+               cr0 |= SSPCR0_SPH;
        cr0 |= dss;
 
        dev_dbg(&master->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
index 50e41f66a2d7077e6d5fe9e736e7e4b2b66d0b18..a35faced045618efaf55ad441496bcdcc3dfd392 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 //
 // Copyright 2013 Freescale Semiconductor, Inc.
+// Copyright 2020 NXP
 //
 // Freescale DSPI driver
 // This file contains a driver for the Freescale DSPI
@@ -26,6 +27,9 @@
 #define SPI_MCR_CLR_TXF                        BIT(11)
 #define SPI_MCR_CLR_RXF                        BIT(10)
 #define SPI_MCR_XSPI                   BIT(3)
+#define SPI_MCR_DIS_TXF                        BIT(13)
+#define SPI_MCR_DIS_RXF                        BIT(12)
+#define SPI_MCR_HALT                   BIT(0)
 
 #define SPI_TCR                                0x08
 #define SPI_TCR_GET_TCNT(x)            (((x) & GENMASK(31, 16)) >> 16)
@@ -246,13 +250,33 @@ struct fsl_dspi {
 
 static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
 {
-       memcpy(txdata, dspi->tx, dspi->oper_word_size);
+       switch (dspi->oper_word_size) {
+       case 1:
+               *txdata = *(u8 *)dspi->tx;
+               break;
+       case 2:
+               *txdata = *(u16 *)dspi->tx;
+               break;
+       case 4:
+               *txdata = *(u32 *)dspi->tx;
+               break;
+       }
        dspi->tx += dspi->oper_word_size;
 }
 
 static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
 {
-       memcpy(dspi->rx, &rxdata, dspi->oper_word_size);
+       switch (dspi->oper_word_size) {
+       case 1:
+               *(u8 *)dspi->rx = rxdata;
+               break;
+       case 2:
+               *(u16 *)dspi->rx = rxdata;
+               break;
+       case 4:
+               *(u32 *)dspi->rx = rxdata;
+               break;
+       }
        dspi->rx += dspi->oper_word_size;
 }
 
@@ -1417,6 +1441,24 @@ static int dspi_remove(struct platform_device *pdev)
        return 0;
 }
 
+static void dspi_shutdown(struct platform_device *pdev)
+{
+       struct spi_controller *ctlr = platform_get_drvdata(pdev);
+       struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr);
+
+       /* Disable RX and TX */
+       regmap_update_bits(dspi->regmap, SPI_MCR,
+                          SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF,
+                          SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF);
+
+       /* Stop Running */
+       regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_HALT, SPI_MCR_HALT);
+
+       dspi_release_dma(dspi);
+       clk_disable_unprepare(dspi->clk);
+       spi_unregister_controller(dspi->ctlr);
+}
+
 static struct platform_driver fsl_dspi_driver = {
        .driver.name            = DRIVER_NAME,
        .driver.of_match_table  = fsl_dspi_dt_ids,
@@ -1424,6 +1466,7 @@ static struct platform_driver fsl_dspi_driver = {
        .driver.pm              = &dspi_pm,
        .probe                  = dspi_probe,
        .remove                 = dspi_remove,
+       .shutdown               = dspi_shutdown,
 };
 module_platform_driver(fsl_dspi_driver);
 
index 8b41b70f6f5c176cdf996e0f5e4c6e02bd2d7ffc..1552b28b9515c4e5c7d8e6426e24dd001fa8dbe7 100644 (file)
@@ -186,14 +186,13 @@ static bool fsl_lpspi_can_dma(struct spi_controller *controller,
 
        bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word);
 
-       switch (bytes_per_word)
-       {
-               case 1:
-               case 2:
-               case 4:
-                       break;
-               default:
-                       return false;
+       switch (bytes_per_word) {
+       case 1:
+       case 2:
+       case 4:
+               break;
+       default:
+               return false;
        }
 
        return true;
@@ -941,7 +940,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(fsl_lpspi->dev);
        if (ret < 0) {
                dev_err(fsl_lpspi->dev, "failed to enable clock\n");
-               goto out_controller_put;
+               goto out_pm_get;
        }
 
        temp = readl(fsl_lpspi->base + IMX7ULP_PARAM);
@@ -950,13 +949,15 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
 
        ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller);
        if (ret == -EPROBE_DEFER)
-               goto out_controller_put;
+               goto out_pm_get;
 
        if (ret < 0)
                dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret);
 
        return 0;
 
+out_pm_get:
+       pm_runtime_put_noidle(fsl_lpspi->dev);
 out_controller_put:
        spi_controller_put(controller);
 
index 02e5cba0a5bb62bbc0e0068e71d02f10c3ade8b1..6766262d7e7519635950c73077f70a77143ba0ce 100644 (file)
@@ -876,14 +876,15 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                        "QuadSPI-memory");
-       q->ahb_addr = devm_ioremap_resource(dev, res);
-       if (IS_ERR(q->ahb_addr)) {
-               ret = PTR_ERR(q->ahb_addr);
+       q->memmap_phy = res->start;
+       /* Since there are 4 cs, map size required is 4 times ahb_buf_size */
+       q->ahb_addr = devm_ioremap(dev, q->memmap_phy,
+                                  (q->devtype_data->ahb_buf_size * 4));
+       if (!q->ahb_addr) {
+               ret = -ENOMEM;
                goto err_put_ctrl;
        }
 
-       q->memmap_phy = res->start;
-
        /* find the clocks */
        q->clk_en = devm_clk_get(dev, "qspi_en");
        if (IS_ERR(q->clk_en)) {
index 3b81772fea0d3e9a11696f20cf7a4ad42a6b5f21..67f022b8c81dc5fbec4b7c99eb5de7b6328a2386 100644 (file)
@@ -588,7 +588,7 @@ static void fsl_spi_grlib_probe(struct device *dev)
        pdata->cs_control = fsl_spi_grlib_cs_control;
 }
 
-static struct spi_master * fsl_spi_probe(struct device *dev,
+static struct spi_master *fsl_spi_probe(struct device *dev,
                struct resource *mem, unsigned int irq)
 {
        struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
index e3b57252d075328600a2596468cef92a90e01630..64a18d08a4d9e4b43f0bf52f7c4dd81d93785f5b 100644 (file)
 
 #define HISI_SFC_V3XX_VERSION (0x1f8)
 
+#define HISI_SFC_V3XX_INT_STAT (0x120)
+#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2)
+#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5)
+#define HISI_SFC_V3XX_INT_CLR (0x12c)
+#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff)
 #define HISI_SFC_V3XX_CMD_CFG (0x300)
 #define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
 #define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17)
@@ -163,7 +168,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
                                         u8 chip_select)
 {
        int ret, len = op->data.nbytes;
-       u32 config = 0;
+       u32 int_stat, config = 0;
 
        if (op->addr.nbytes)
                config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
@@ -228,6 +233,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
        if (ret)
                return ret;
 
+       /*
+        * The interrupt status register indicates whether an error occurs
+        * after per operation. Check it, and clear the interrupts for
+        * next time judgement.
+        */
+       int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT);
+       writel(HISI_SFC_V3XX_INT_CLR_CLEAR,
+              host->regbase + HISI_SFC_V3XX_INT_CLR);
+
+       if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) {
+               dev_err(host->dev, "fail to access protected address\n");
+               return -EIO;
+       }
+
+       if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) {
+               dev_err(host->dev, "page program operation failed\n");
+               return -EIO;
+       }
+
        if (op->data.dir == SPI_MEM_DATA_IN)
                hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
 
index f4f28a400a96832b2c11a548a5957b43066fa107..b7a85e3fe1c16ef06a178119221c9d43ca527b5d 100644 (file)
@@ -71,6 +71,7 @@ struct spi_imx_devtype_data {
        void (*reset)(struct spi_imx_data *);
        void (*setup_wml)(struct spi_imx_data *);
        void (*disable)(struct spi_imx_data *);
+       void (*disable_dma)(struct spi_imx_data *);
        bool has_dmamode;
        bool has_slavemode;
        unsigned int fifo_size;
@@ -485,6 +486,11 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
        writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
 }
 
+static void mx51_disable_dma(struct spi_imx_data *spi_imx)
+{
+       writel(0, spi_imx->base + MX51_ECSPI_DMA);
+}
+
 static void mx51_ecspi_disable(struct spi_imx_data *spi_imx)
 {
        u32 ctrl;
@@ -987,6 +993,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
        .rx_available = mx51_ecspi_rx_available,
        .reset = mx51_ecspi_reset,
        .setup_wml = mx51_setup_wml,
+       .disable_dma = mx51_disable_dma,
        .fifo_size = 64,
        .has_dmamode = true,
        .dynamic_burst = true,
@@ -1001,6 +1008,7 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
        .prepare_transfer = mx51_ecspi_prepare_transfer,
        .trigger = mx51_ecspi_trigger,
        .rx_available = mx51_ecspi_rx_available,
+       .disable_dma = mx51_disable_dma,
        .reset = mx51_ecspi_reset,
        .fifo_size = 64,
        .has_dmamode = true,
@@ -1385,6 +1393,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
                                DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!desc_tx) {
                dmaengine_terminate_all(master->dma_tx);
+               dmaengine_terminate_all(master->dma_rx);
                return -EINVAL;
        }
 
@@ -1498,6 +1507,7 @@ static int spi_imx_transfer(struct spi_device *spi,
                                struct spi_transfer *transfer)
 {
        struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
+       int ret;
 
        /* flush rxfifo before transfer */
        while (spi_imx->devtype_data->rx_available(spi_imx))
@@ -1506,10 +1516,23 @@ static int spi_imx_transfer(struct spi_device *spi,
        if (spi_imx->slave_mode)
                return spi_imx_pio_transfer_slave(spi, transfer);
 
-       if (spi_imx->usedma)
-               return spi_imx_dma_transfer(spi_imx, transfer);
-       else
-               return spi_imx_pio_transfer(spi, transfer);
+       /*
+        * fallback PIO mode if dma setup error happen, for example sdma
+        * firmware may not be updated as ERR009165 required.
+        */
+       if (spi_imx->usedma) {
+               ret = spi_imx_dma_transfer(spi_imx, transfer);
+               if (ret != -EINVAL)
+                       return ret;
+
+               spi_imx->devtype_data->disable_dma(spi_imx);
+
+               spi_imx->usedma = false;
+               spi_imx->dynamic_burst = spi_imx->devtype_data->dynamic_burst;
+               dev_dbg(&spi->dev, "Fallback to PIO mode\n");
+       }
+
+       return spi_imx_pio_transfer(spi, transfer);
 }
 
 static int spi_imx_setup(struct spi_device *spi)
index adaa0c49f966da97cebd5547449018d491bc9a59..9a86cc27fcc056bc5cdeef9464a10c923fae2dcc 100644 (file)
@@ -108,15 +108,17 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
                return 0;
 
        case 2:
-               if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
-                   (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+               if ((tx &&
+                    (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) ||
+                   (!tx &&
+                    (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))))
                        return 0;
 
                break;
 
        case 4:
-               if ((tx && (mode & SPI_TX_QUAD)) ||
-                   (!tx && (mode & SPI_RX_QUAD)))
+               if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) ||
+                   (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL))))
                        return 0;
 
                break;
index c15a9910549f7d30e5b660ff8bd6dc384e5ff161..7bc302b50396ac2170b8bc41cf27a12844637db9 100644 (file)
@@ -391,7 +391,7 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp,
        return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE);
 }
 
-int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
 {
        struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master);
        int ret;
index 4f94c9127fc10fd33ab5d98689d8da1041104fb7..cc9ef371db14c2db20fefb9e1d5d3e4f30e927dc 100644 (file)
@@ -51,6 +51,10 @@ static int spi_mux_select(struct spi_device *spi)
        struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller);
        int ret;
 
+       ret = mux_control_select(priv->mux, spi->chip_select);
+       if (ret)
+               return ret;
+
        if (priv->current_cs == spi->chip_select)
                return 0;
 
@@ -62,10 +66,6 @@ static int spi_mux_select(struct spi_device *spi)
        priv->spi->mode = spi->mode;
        priv->spi->bits_per_word = spi->bits_per_word;
 
-       ret = mux_control_select(priv->mux, spi->chip_select);
-       if (ret)
-               return ret;
-
        priv->current_cs = spi->chip_select;
 
        return 0;
index 1f59beb7d27ecb5eed72ba250fcf7c7304ff3810..43f73db22f21092b2f1be8c7854c2a24c337d59d 100644 (file)
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/clk.h>
 #include <linux/sizes.h>
-#include <linux/gpio.h>
 #include <asm/unaligned.h>
 
 #define DRIVER_NAME                    "orion_spi"
@@ -98,7 +96,6 @@ struct orion_spi {
        struct clk              *clk;
        struct clk              *axi_clk;
        const struct orion_spi_dev *devdata;
-       int                     unused_hw_gpio;
 
        struct orion_child_options      child[ORION_NUM_CHIPSELECTS];
 };
@@ -325,20 +322,27 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 static void orion_spi_set_cs(struct spi_device *spi, bool enable)
 {
        struct orion_spi *orion_spi;
-       int cs;
 
        orion_spi = spi_master_get_devdata(spi->master);
 
-       if (gpio_is_valid(spi->cs_gpio))
-               cs = orion_spi->unused_hw_gpio;
-       else
-               cs = spi->chip_select;
-
+       /*
+        * If this line is using a GPIO to control chip select, this internal
+        * .set_cs() function will still be called, so we clear any previous
+        * chip select. The CS we activate will not have any elecrical effect,
+        * as it is handled by a GPIO, but that doesn't matter. What we need
+        * is to deassert the old chip select and assert some other chip select.
+        */
        orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK);
        orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG,
-                               ORION_SPI_CS(cs));
+                         ORION_SPI_CS(spi->chip_select));
 
-       /* Chip select logic is inverted from spi_set_cs */
+       /*
+        * Chip select logic is inverted from spi_set_cs(). For lines using a
+        * GPIO to do chip select SPI_CS_HIGH is enforced and inversion happens
+        * in the GPIO library, but we don't care about that, because in those
+        * cases we are dealing with an unused native CS anyways so the polarity
+        * doesn't matter.
+        */
        if (!enable)
                orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
        else
@@ -503,9 +507,6 @@ static int orion_spi_transfer_one(struct spi_master *master,
 
 static int orion_spi_setup(struct spi_device *spi)
 {
-       if (gpio_is_valid(spi->cs_gpio)) {
-               gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
-       }
        return orion_spi_setup_transfer(spi, NULL);
 }
 
@@ -622,13 +623,13 @@ static int orion_spi_probe(struct platform_device *pdev)
        master->setup = orion_spi_setup;
        master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
        master->auto_runtime_pm = true;
+       master->use_gpio_descriptors = true;
        master->flags = SPI_MASTER_GPIO_SS;
 
        platform_set_drvdata(pdev, master);
 
        spi = spi_master_get_devdata(master);
        spi->master = master;
-       spi->unused_hw_gpio = -1;
 
        of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
        devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
@@ -683,7 +684,6 @@ static int orion_spi_probe(struct platform_device *pdev)
        for_each_available_child_of_node(pdev->dev.of_node, np) {
                struct orion_direct_acc *dir_acc;
                u32 cs;
-               int cs_gpio;
 
                /* Get chip-select number from the "reg" property */
                status = of_property_read_u32(np, "reg", &cs);
@@ -694,44 +694,6 @@ static int orion_spi_probe(struct platform_device *pdev)
                        continue;
                }
 
-               /*
-                * Initialize the CS GPIO:
-                * - properly request the actual GPIO signal
-                * - de-assert the logical signal so that all GPIO CS lines
-                *   are inactive when probing for slaves
-                * - find an unused physical CS which will be driven for any
-                *   slave which uses a CS GPIO
-                */
-               cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", cs);
-               if (cs_gpio > 0) {
-                       char *gpio_name;
-                       int cs_flags;
-
-                       if (spi->unused_hw_gpio == -1) {
-                               dev_info(&pdev->dev,
-                                       "Selected unused HW CS#%d for any GPIO CSes\n",
-                                       cs);
-                               spi->unused_hw_gpio = cs;
-                       }
-
-                       gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
-                                       "%s-CS%d", dev_name(&pdev->dev), cs);
-                       if (!gpio_name) {
-                               status = -ENOMEM;
-                               goto out_rel_axi_clk;
-                       }
-
-                       cs_flags = of_property_read_bool(np, "spi-cs-high") ?
-                               GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
-                       status = devm_gpio_request_one(&pdev->dev, cs_gpio,
-                                       cs_flags, gpio_name);
-                       if (status) {
-                               dev_err(&pdev->dev,
-                                       "Can't request GPIO for CS %d\n", cs);
-                               goto out_rel_axi_clk;
-                       }
-               }
-
                /*
                 * Check if an address is configured for this SPI device. If
                 * not, the MBus mapping via the 'ranges' property in the 'soc'
index 73d2a65d0b6eff66a9a692580ff08b4f46c55ff8..6721910e5f2aaaf44682e9825f9ef3871eed6060 100644 (file)
@@ -150,6 +150,7 @@ static const struct lpss_config lpss_platforms[] = {
                .tx_threshold_hi = 48,
                .cs_sel_shift = 8,
                .cs_sel_mask = 3 << 8,
+               .cs_clk_stays_gated = true,
        },
        {       /* LPSS_CNL_SSP */
                .offset = 0x200,
@@ -1884,7 +1885,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
 
        /* Register with the SPI framework */
        platform_set_drvdata(pdev, drv_data);
-       status = devm_spi_register_controller(&pdev->dev, controller);
+       status = spi_register_controller(controller);
        if (status != 0) {
                dev_err(&pdev->dev, "problem registering spi controller\n");
                goto out_error_pm_runtime_enabled;
@@ -1893,7 +1894,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
        return status;
 
 out_error_pm_runtime_enabled:
-       pm_runtime_put_noidle(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
 out_error_clock_enabled:
@@ -1916,6 +1916,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
 
        pm_runtime_get_sync(&pdev->dev);
 
+       spi_unregister_controller(drv_data->controller);
+
        /* Disable the SSP at the peripheral and SOC level */
        pxa2xx_spi_write(drv_data, SSCR0, 0);
        clk_disable_unprepare(ssp->clk);
index 4c9620e0d18ccdbb1555c65eb4851c44ea1bb513..8aa51beb4ff3ebd72324b541f70697c92b7d09ac 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/spi/spi.h>
+#include <linux/of.h>
 
 #include <asm/mach-ath79/ar71xx_regs.h>
 
@@ -150,6 +151,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
        if (IS_ERR(ahb_clk))
                return PTR_ERR(ahb_clk);
 
+       master->dev.of_node = pdev->dev.of_node;
        master->bus_num = 0;
        master->num_chipselect = 3;
        master->mode_bits = SPI_TX_DUAL;
@@ -158,6 +160,11 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
        master->transfer_one = rb4xx_transfer_one;
        master->set_cs = rb4xx_set_cs;
 
+       rbspi = spi_master_get_devdata(master);
+       rbspi->base = spi_base;
+       rbspi->clk = ahb_clk;
+       platform_set_drvdata(pdev, rbspi);
+
        err = devm_spi_register_master(&pdev->dev, master);
        if (err) {
                dev_err(&pdev->dev, "failed to register SPI master\n");
@@ -168,11 +175,6 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       rbspi = spi_master_get_devdata(master);
-       rbspi->base = spi_base;
-       rbspi->clk = ahb_clk;
-       platform_set_drvdata(pdev, rbspi);
-
        /* Enable SPI */
        rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
 
@@ -188,11 +190,18 @@ static int rb4xx_spi_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id rb4xx_spi_dt_match[] = {
+       { .compatible = "mikrotik,rb4xx-spi" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match);
+
 static struct platform_driver rb4xx_spi_drv = {
        .probe = rb4xx_spi_probe,
        .remove = rb4xx_spi_remove,
        .driver = {
                .name = "rb4xx-spi",
+               .of_match_table = of_match_ptr(rb4xx_spi_dt_match),
        },
 };
 
index 70ef63e0b6b8d27fbd224f10db34670e84cfea62..9b8a5e1233c0661956c9a87aee97721d478de062 100644 (file)
@@ -183,6 +183,8 @@ struct rockchip_spi {
        u8 rsd;
 
        bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
+
+       bool slave_abort;
 };
 
 static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
@@ -219,8 +221,8 @@ static u32 get_fifo_len(struct rockchip_spi *rs)
 
 static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
 {
-       struct spi_master *master = spi->master;
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = spi->controller;
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
        bool cs_asserted = !enable;
 
        /* Return immediately for no-op */
@@ -244,10 +246,10 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
        rs->cs_asserted[spi->chip_select] = cs_asserted;
 }
 
-static void rockchip_spi_handle_err(struct spi_master *master,
+static void rockchip_spi_handle_err(struct spi_controller *ctlr,
                                    struct spi_message *msg)
 {
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        /* stop running spi transfer
         * this also flushes both rx and tx fifos
@@ -258,10 +260,10 @@ static void rockchip_spi_handle_err(struct spi_master *master,
        writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
 
        if (atomic_read(&rs->state) & TXDMA)
-               dmaengine_terminate_async(master->dma_tx);
+               dmaengine_terminate_async(ctlr->dma_tx);
 
        if (atomic_read(&rs->state) & RXDMA)
-               dmaengine_terminate_async(master->dma_rx);
+               dmaengine_terminate_async(ctlr->dma_rx);
 }
 
 static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
@@ -319,8 +321,8 @@ static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
 
 static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
 {
-       struct spi_master *master = dev_id;
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = dev_id;
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        if (rs->tx_left)
                rockchip_spi_pio_writer(rs);
@@ -329,7 +331,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
        if (!rs->rx_left) {
                spi_enable_chip(rs, false);
                writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
-               spi_finalize_current_transfer(master);
+               spi_finalize_current_transfer(ctlr);
        }
 
        return IRQ_HANDLED;
@@ -355,35 +357,35 @@ static int rockchip_spi_prepare_irq(struct rockchip_spi *rs,
 
 static void rockchip_spi_dma_rxcb(void *data)
 {
-       struct spi_master *master = data;
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = data;
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
        int state = atomic_fetch_andnot(RXDMA, &rs->state);
 
-       if (state & TXDMA)
+       if (state & TXDMA && !rs->slave_abort)
                return;
 
        spi_enable_chip(rs, false);
-       spi_finalize_current_transfer(master);
+       spi_finalize_current_transfer(ctlr);
 }
 
 static void rockchip_spi_dma_txcb(void *data)
 {
-       struct spi_master *master = data;
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = data;
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
        int state = atomic_fetch_andnot(TXDMA, &rs->state);
 
-       if (state & RXDMA)
+       if (state & RXDMA && !rs->slave_abort)
                return;
 
        /* Wait until the FIFO data completely. */
        wait_for_idle(rs);
 
        spi_enable_chip(rs, false);
-       spi_finalize_current_transfer(master);
+       spi_finalize_current_transfer(ctlr);
 }
 
 static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
-               struct spi_master *master, struct spi_transfer *xfer)
+               struct spi_controller *ctlr, struct spi_transfer *xfer)
 {
        struct dma_async_tx_descriptor *rxdesc, *txdesc;
 
@@ -398,17 +400,17 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
                        .src_maxburst = 1,
                };
 
-               dmaengine_slave_config(master->dma_rx, &rxconf);
+               dmaengine_slave_config(ctlr->dma_rx, &rxconf);
 
                rxdesc = dmaengine_prep_slave_sg(
-                               master->dma_rx,
+                               ctlr->dma_rx,
                                xfer->rx_sg.sgl, xfer->rx_sg.nents,
                                DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
                if (!rxdesc)
                        return -EINVAL;
 
                rxdesc->callback = rockchip_spi_dma_rxcb;
-               rxdesc->callback_param = master;
+               rxdesc->callback_param = ctlr;
        }
 
        txdesc = NULL;
@@ -420,27 +422,27 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
                        .dst_maxburst = rs->fifo_len / 4,
                };
 
-               dmaengine_slave_config(master->dma_tx, &txconf);
+               dmaengine_slave_config(ctlr->dma_tx, &txconf);
 
                txdesc = dmaengine_prep_slave_sg(
-                               master->dma_tx,
+                               ctlr->dma_tx,
                                xfer->tx_sg.sgl, xfer->tx_sg.nents,
                                DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
                if (!txdesc) {
                        if (rxdesc)
-                               dmaengine_terminate_sync(master->dma_rx);
+                               dmaengine_terminate_sync(ctlr->dma_rx);
                        return -EINVAL;
                }
 
                txdesc->callback = rockchip_spi_dma_txcb;
-               txdesc->callback_param = master;
+               txdesc->callback_param = ctlr;
        }
 
        /* rx must be started before tx due to spi instinct */
        if (rxdesc) {
                atomic_or(RXDMA, &rs->state);
                dmaengine_submit(rxdesc);
-               dma_async_issue_pending(master->dma_rx);
+               dma_async_issue_pending(ctlr->dma_rx);
        }
 
        spi_enable_chip(rs, true);
@@ -448,7 +450,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
        if (txdesc) {
                atomic_or(TXDMA, &rs->state);
                dmaengine_submit(txdesc);
-               dma_async_issue_pending(master->dma_tx);
+               dma_async_issue_pending(ctlr->dma_tx);
        }
 
        /* 1 means the transfer is in progress */
@@ -457,7 +459,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
 
 static void rockchip_spi_config(struct rockchip_spi *rs,
                struct spi_device *spi, struct spi_transfer *xfer,
-               bool use_dma)
+               bool use_dma, bool slave_mode)
 {
        u32 cr0 = CR0_FRF_SPI  << CR0_FRF_OFFSET
                | CR0_BHT_8BIT << CR0_BHT_OFFSET
@@ -466,6 +468,10 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
        u32 cr1;
        u32 dmacr = 0;
 
+       if (slave_mode)
+               cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET;
+       rs->slave_abort = false;
+
        cr0 |= rs->rsd << CR0_RSD_OFFSET;
        cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
        if (spi->mode & SPI_LSB_FIRST)
@@ -493,7 +499,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
                break;
        default:
                /* we only whitelist 4, 8 and 16 bit words in
-                * master->bits_per_word_mask, so this shouldn't
+                * ctlr->bits_per_word_mask, so this shouldn't
                 * happen
                 */
                unreachable();
@@ -535,12 +541,22 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
        return ROCKCHIP_SPI_MAX_TRANLEN;
 }
 
+static int rockchip_spi_slave_abort(struct spi_controller *ctlr)
+{
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+
+       rs->slave_abort = true;
+       complete(&ctlr->xfer_completion);
+
+       return 0;
+}
+
 static int rockchip_spi_transfer_one(
-               struct spi_master *master,
+               struct spi_controller *ctlr,
                struct spi_device *spi,
                struct spi_transfer *xfer)
 {
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
        bool use_dma;
 
        WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
@@ -558,21 +574,21 @@ static int rockchip_spi_transfer_one(
 
        rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
 
-       use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false;
+       use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
 
-       rockchip_spi_config(rs, spi, xfer, use_dma);
+       rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
 
        if (use_dma)
-               return rockchip_spi_prepare_dma(rs, master, xfer);
+               return rockchip_spi_prepare_dma(rs, ctlr, xfer);
 
        return rockchip_spi_prepare_irq(rs, xfer);
 }
 
-static bool rockchip_spi_can_dma(struct spi_master *master,
+static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
                                 struct spi_device *spi,
                                 struct spi_transfer *xfer)
 {
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
        unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2;
 
        /* if the numbor of spi words to transfer is less than the fifo
@@ -586,44 +602,55 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 {
        int ret;
        struct rockchip_spi *rs;
-       struct spi_master *master;
+       struct spi_controller *ctlr;
        struct resource *mem;
+       struct device_node *np = pdev->dev.of_node;
        u32 rsd_nsecs;
+       bool slave_mode;
+
+       slave_mode = of_property_read_bool(np, "spi-slave");
+
+       if (slave_mode)
+               ctlr = spi_alloc_slave(&pdev->dev,
+                               sizeof(struct rockchip_spi));
+       else
+               ctlr = spi_alloc_master(&pdev->dev,
+                               sizeof(struct rockchip_spi));
 
-       master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
-       if (!master)
+       if (!ctlr)
                return -ENOMEM;
 
-       platform_set_drvdata(pdev, master);
+       platform_set_drvdata(pdev, ctlr);
 
-       rs = spi_master_get_devdata(master);
+       rs = spi_controller_get_devdata(ctlr);
+       ctlr->slave = slave_mode;
 
        /* Get basic io resource and map it */
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        rs->regs = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(rs->regs)) {
                ret =  PTR_ERR(rs->regs);
-               goto err_put_master;
+               goto err_put_ctlr;
        }
 
        rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
        if (IS_ERR(rs->apb_pclk)) {
                dev_err(&pdev->dev, "Failed to get apb_pclk\n");
                ret = PTR_ERR(rs->apb_pclk);
-               goto err_put_master;
+               goto err_put_ctlr;
        }
 
        rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
        if (IS_ERR(rs->spiclk)) {
                dev_err(&pdev->dev, "Failed to get spi_pclk\n");
                ret = PTR_ERR(rs->spiclk);
-               goto err_put_master;
+               goto err_put_ctlr;
        }
 
        ret = clk_prepare_enable(rs->apb_pclk);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
-               goto err_put_master;
+               goto err_put_ctlr;
        }
 
        ret = clk_prepare_enable(rs->spiclk);
@@ -639,7 +666,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
                goto err_disable_spiclk;
 
        ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL,
-                       IRQF_ONESHOT, dev_name(&pdev->dev), master);
+                       IRQF_ONESHOT, dev_name(&pdev->dev), ctlr);
        if (ret)
                goto err_disable_spiclk;
 
@@ -673,78 +700,90 @@ static int rockchip_spi_probe(struct platform_device *pdev)
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
 
-       master->auto_runtime_pm = true;
-       master->bus_num = pdev->id;
-       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
-       master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM;
-       master->dev.of_node = pdev->dev.of_node;
-       master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4);
-       master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
-       master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
-
-       master->set_cs = rockchip_spi_set_cs;
-       master->transfer_one = rockchip_spi_transfer_one;
-       master->max_transfer_size = rockchip_spi_max_transfer_size;
-       master->handle_err = rockchip_spi_handle_err;
-       master->flags = SPI_MASTER_GPIO_SS;
-
-       master->dma_tx = dma_request_chan(rs->dev, "tx");
-       if (IS_ERR(master->dma_tx)) {
+       ctlr->auto_runtime_pm = true;
+       ctlr->bus_num = pdev->id;
+       ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
+       if (slave_mode) {
+               ctlr->mode_bits |= SPI_NO_CS;
+               ctlr->slave_abort = rockchip_spi_slave_abort;
+       } else {
+               ctlr->flags = SPI_MASTER_GPIO_SS;
+               ctlr->max_native_cs = ROCKCHIP_SPI_MAX_CS_NUM;
+               /*
+                * rk spi0 has two native cs, spi1..5 one cs only
+                * if num-cs is missing in the dts, default to 1
+                */
+               if (of_property_read_u16(np, "num-cs", &ctlr->num_chipselect))
+                       ctlr->num_chipselect = 1;
+               ctlr->use_gpio_descriptors = true;
+       }
+       ctlr->dev.of_node = pdev->dev.of_node;
+       ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4);
+       ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
+       ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
+
+       ctlr->set_cs = rockchip_spi_set_cs;
+       ctlr->transfer_one = rockchip_spi_transfer_one;
+       ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
+       ctlr->handle_err = rockchip_spi_handle_err;
+
+       ctlr->dma_tx = dma_request_chan(rs->dev, "tx");
+       if (IS_ERR(ctlr->dma_tx)) {
                /* Check tx to see if we need defer probing driver */
-               if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) {
+               if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) {
                        ret = -EPROBE_DEFER;
                        goto err_disable_pm_runtime;
                }
                dev_warn(rs->dev, "Failed to request TX DMA channel\n");
-               master->dma_tx = NULL;
+               ctlr->dma_tx = NULL;
        }
 
-       master->dma_rx = dma_request_chan(rs->dev, "rx");
-       if (IS_ERR(master->dma_rx)) {
-               if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) {
+       ctlr->dma_rx = dma_request_chan(rs->dev, "rx");
+       if (IS_ERR(ctlr->dma_rx)) {
+               if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) {
                        ret = -EPROBE_DEFER;
                        goto err_free_dma_tx;
                }
                dev_warn(rs->dev, "Failed to request RX DMA channel\n");
-               master->dma_rx = NULL;
+               ctlr->dma_rx = NULL;
        }
 
-       if (master->dma_tx && master->dma_rx) {
+       if (ctlr->dma_tx && ctlr->dma_rx) {
                rs->dma_addr_tx = mem->start + ROCKCHIP_SPI_TXDR;
                rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR;
-               master->can_dma = rockchip_spi_can_dma;
+               ctlr->can_dma = rockchip_spi_can_dma;
        }
 
-       ret = devm_spi_register_master(&pdev->dev, master);
+       ret = devm_spi_register_controller(&pdev->dev, ctlr);
        if (ret < 0) {
-               dev_err(&pdev->dev, "Failed to register master\n");
+               dev_err(&pdev->dev, "Failed to register controller\n");
                goto err_free_dma_rx;
        }
 
        return 0;
 
 err_free_dma_rx:
-       if (master->dma_rx)
-               dma_release_channel(master->dma_rx);
+       if (ctlr->dma_rx)
+               dma_release_channel(ctlr->dma_rx);
 err_free_dma_tx:
-       if (master->dma_tx)
-               dma_release_channel(master->dma_tx);
+       if (ctlr->dma_tx)
+               dma_release_channel(ctlr->dma_tx);
 err_disable_pm_runtime:
        pm_runtime_disable(&pdev->dev);
 err_disable_spiclk:
        clk_disable_unprepare(rs->spiclk);
 err_disable_apbclk:
        clk_disable_unprepare(rs->apb_pclk);
-err_put_master:
-       spi_master_put(master);
+err_put_ctlr:
+       spi_controller_put(ctlr);
 
        return ret;
 }
 
 static int rockchip_spi_remove(struct platform_device *pdev)
 {
-       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = spi_controller_get(platform_get_drvdata(pdev));
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        pm_runtime_get_sync(&pdev->dev);
 
@@ -755,12 +794,12 @@ static int rockchip_spi_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
 
-       if (master->dma_tx)
-               dma_release_channel(master->dma_tx);
-       if (master->dma_rx)
-               dma_release_channel(master->dma_rx);
+       if (ctlr->dma_tx)
+               dma_release_channel(ctlr->dma_tx);
+       if (ctlr->dma_rx)
+               dma_release_channel(ctlr->dma_rx);
 
-       spi_master_put(master);
+       spi_controller_put(ctlr);
 
        return 0;
 }
@@ -769,9 +808,9 @@ static int rockchip_spi_remove(struct platform_device *pdev)
 static int rockchip_spi_suspend(struct device *dev)
 {
        int ret;
-       struct spi_master *master = dev_get_drvdata(dev);
+       struct spi_controller *ctlr = dev_get_drvdata(dev);
 
-       ret = spi_master_suspend(master);
+       ret = spi_controller_suspend(ctlr);
        if (ret < 0)
                return ret;
 
@@ -787,8 +826,8 @@ static int rockchip_spi_suspend(struct device *dev)
 static int rockchip_spi_resume(struct device *dev)
 {
        int ret;
-       struct spi_master *master = dev_get_drvdata(dev);
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = dev_get_drvdata(dev);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        pinctrl_pm_select_default_state(dev);
 
@@ -796,7 +835,7 @@ static int rockchip_spi_resume(struct device *dev)
        if (ret < 0)
                return ret;
 
-       ret = spi_master_resume(master);
+       ret = spi_controller_resume(ctlr);
        if (ret < 0) {
                clk_disable_unprepare(rs->spiclk);
                clk_disable_unprepare(rs->apb_pclk);
@@ -809,8 +848,8 @@ static int rockchip_spi_resume(struct device *dev)
 #ifdef CONFIG_PM
 static int rockchip_spi_runtime_suspend(struct device *dev)
 {
-       struct spi_master *master = dev_get_drvdata(dev);
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = dev_get_drvdata(dev);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        clk_disable_unprepare(rs->spiclk);
        clk_disable_unprepare(rs->apb_pclk);
@@ -821,8 +860,8 @@ static int rockchip_spi_runtime_suspend(struct device *dev)
 static int rockchip_spi_runtime_resume(struct device *dev)
 {
        int ret;
-       struct spi_master *master = dev_get_drvdata(dev);
-       struct rockchip_spi *rs = spi_master_get_devdata(master);
+       struct spi_controller *ctlr = dev_get_drvdata(dev);
+       struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
        ret = clk_prepare_enable(rs->apb_pclk);
        if (ret < 0)
index 5497eeb3bf3e9166f22c1494e585a33aee3674f3..ee0f3edf49cdbdd72b050ac4d48d4a87481b01bc 100644 (file)
@@ -345,6 +345,6 @@ static struct i2c_driver sc18is602_driver = {
 
 module_i2c_driver(sc18is602_driver);
 
-MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver");
+MODULE_DESCRIPTION("SC18IS602/603 SPI Master Driver");
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_LICENSE("GPL");
index 1c11a00a2c36788e5d03955e58851f3b81c17969..b2579af0e3eb0e39fac22464eea39597b2c9169e 100644 (file)
@@ -1398,7 +1398,7 @@ static int sh_msiof_spi_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
                         sh_msiof_spi_resume);
-#define DEV_PM_OPS     &sh_msiof_spi_pm_ops
+#define DEV_PM_OPS     (&sh_msiof_spi_pm_ops)
 #else
 #define DEV_PM_OPS     NULL
 #endif /* CONFIG_PM_SLEEP */
index 87dadb6b8ebf457576c09bd4aba55b84400637ab..88e6543648cb8627067b0179a9a419a591376c7f 100644 (file)
@@ -319,7 +319,7 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
 
 static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi)
 {
-#ifdef CONFIG_SPRD_WATCHDOG
+#if IS_ENABLED(CONFIG_SPRD_WATCHDOG)
        u32 val;
 
        /* Set default watchdog reboot mode */
index d066f5144c3edc8b9c1766b09832958d4e652e32..3c44bb2fd9b14bcc8f1865f29de0ac14267344cd 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/sizes.h>
@@ -87,6 +88,7 @@
 #define STM32_BUSY_TIMEOUT_US 100000
 #define STM32_ABT_TIMEOUT_US 100000
 #define STM32_COMP_TIMEOUT_MS 1000
+#define STM32_AUTOSUSPEND_DELAY -1
 
 struct stm32_qspi_flash {
        struct stm32_qspi *qspi;
@@ -431,10 +433,17 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
        struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
        int ret;
 
+       ret = pm_runtime_get_sync(qspi->dev);
+       if (ret < 0)
+               return ret;
+
        mutex_lock(&qspi->lock);
        ret = stm32_qspi_send(mem, op);
        mutex_unlock(&qspi->lock);
 
+       pm_runtime_mark_last_busy(qspi->dev);
+       pm_runtime_put_autosuspend(qspi->dev);
+
        return ret;
 }
 
@@ -444,6 +453,7 @@ static int stm32_qspi_setup(struct spi_device *spi)
        struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);
        struct stm32_qspi_flash *flash;
        u32 presc;
+       int ret;
 
        if (ctrl->busy)
                return -EBUSY;
@@ -451,6 +461,10 @@ static int stm32_qspi_setup(struct spi_device *spi)
        if (!spi->max_speed_hz)
                return -EINVAL;
 
+       ret = pm_runtime_get_sync(qspi->dev);
+       if (ret < 0)
+               return ret;
+
        presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1;
 
        flash = &qspi->flash[spi->chip_select];
@@ -467,6 +481,9 @@ static int stm32_qspi_setup(struct spi_device *spi)
        writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
        mutex_unlock(&qspi->lock);
 
+       pm_runtime_mark_last_busy(qspi->dev);
+       pm_runtime_put_autosuspend(qspi->dev);
+
        return 0;
 }
 
@@ -538,10 +555,15 @@ static const struct spi_controller_mem_ops stm32_qspi_mem_ops = {
 
 static void stm32_qspi_release(struct stm32_qspi *qspi)
 {
+       pm_runtime_get_sync(qspi->dev);
        /* disable qspi */
        writel_relaxed(0, qspi->io_base + QSPI_CR);
        stm32_qspi_dma_free(qspi);
        mutex_destroy(&qspi->lock);
+       pm_runtime_put_noidle(qspi->dev);
+       pm_runtime_disable(qspi->dev);
+       pm_runtime_set_suspended(qspi->dev);
+       pm_runtime_dont_use_autosuspend(qspi->dev);
        clk_disable_unprepare(qspi->clk);
 }
 
@@ -643,9 +665,20 @@ static int stm32_qspi_probe(struct platform_device *pdev)
        ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP;
        ctrl->dev.of_node = dev->of_node;
 
+       pm_runtime_set_autosuspend_delay(dev, STM32_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_get_noresume(dev);
+
        ret = devm_spi_register_master(dev, ctrl);
-       if (!ret)
-               return 0;
+       if (ret)
+               goto err_qspi_release;
+
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       return 0;
 
 err_qspi_release:
        stm32_qspi_release(qspi);
@@ -660,14 +693,28 @@ static int stm32_qspi_remove(struct platform_device *pdev)
        struct stm32_qspi *qspi = platform_get_drvdata(pdev);
 
        stm32_qspi_release(qspi);
+
        return 0;
 }
 
-static int __maybe_unused stm32_qspi_suspend(struct device *dev)
+static int __maybe_unused stm32_qspi_runtime_suspend(struct device *dev)
 {
        struct stm32_qspi *qspi = dev_get_drvdata(dev);
 
        clk_disable_unprepare(qspi->clk);
+
+       return 0;
+}
+
+static int __maybe_unused stm32_qspi_runtime_resume(struct device *dev)
+{
+       struct stm32_qspi *qspi = dev_get_drvdata(dev);
+
+       return clk_prepare_enable(qspi->clk);
+}
+
+static int __maybe_unused stm32_qspi_suspend(struct device *dev)
+{
        pinctrl_pm_select_sleep_state(dev);
 
        return 0;
@@ -683,10 +730,17 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev)
        writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
        writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
 
+       pm_runtime_mark_last_busy(qspi->dev);
+       pm_runtime_put_autosuspend(qspi->dev);
+
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(stm32_qspi_pm_ops, stm32_qspi_suspend, stm32_qspi_resume);
+static const struct dev_pm_ops stm32_qspi_pm_ops = {
+       SET_RUNTIME_PM_OPS(stm32_qspi_runtime_suspend,
+                          stm32_qspi_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(stm32_qspi_suspend, stm32_qspi_resume)
+};
 
 static const struct of_device_id stm32_qspi_match[] = {
        {.compatible = "st,stm32f469-qspi"},
index 44ac6eb3298d4af6916dd4e1e6df8d6ec2e91dab..4c643dfc7fbbcc500fb301df5956d0b220e966a2 100644 (file)
@@ -811,7 +811,9 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id)
                mask |= STM32F4_SPI_SR_TXE;
        }
 
-       if (!spi->cur_usedma && spi->cur_comm == SPI_FULL_DUPLEX) {
+       if (!spi->cur_usedma && (spi->cur_comm == SPI_FULL_DUPLEX ||
+                               spi->cur_comm == SPI_SIMPLEX_RX ||
+                               spi->cur_comm == SPI_3WIRE_RX)) {
                /* TXE flag is set and is handled when RXNE flag occurs */
                sr &= ~STM32F4_SPI_SR_TXE;
                mask |= STM32F4_SPI_SR_RXNE | STM32F4_SPI_SR_OVR;
@@ -850,7 +852,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id)
                stm32f4_spi_read_rx(spi);
                if (spi->rx_len == 0)
                        end = true;
-               else /* Load data for discontinuous mode */
+               else if (spi->tx_buf)/* Load data for discontinuous mode */
                        stm32f4_spi_write_tx(spi);
        }
 
@@ -1151,7 +1153,9 @@ static int stm32f4_spi_transfer_one_irq(struct stm32_spi *spi)
        /* Enable the interrupts relative to the current communication mode */
        if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) {
                cr2 |= STM32F4_SPI_CR2_TXEIE;
-       } else if (spi->cur_comm == SPI_FULL_DUPLEX) {
+       } else if (spi->cur_comm == SPI_FULL_DUPLEX ||
+                               spi->cur_comm == SPI_SIMPLEX_RX ||
+                               spi->cur_comm == SPI_3WIRE_RX) {
                /* In transmit-only mode, the OVR flag is set in the SR register
                 * since the received data are never read. Therefore set OVR
                 * interrupt only when rx buffer is available.
@@ -1462,10 +1466,16 @@ static int stm32f4_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type)
                stm32_spi_set_bits(spi, STM32F4_SPI_CR1,
                                        STM32F4_SPI_CR1_BIDIMODE |
                                        STM32F4_SPI_CR1_BIDIOE);
-       } else if (comm_type == SPI_FULL_DUPLEX) {
+       } else if (comm_type == SPI_FULL_DUPLEX ||
+                               comm_type == SPI_SIMPLEX_RX) {
                stm32_spi_clr_bits(spi, STM32F4_SPI_CR1,
                                        STM32F4_SPI_CR1_BIDIMODE |
                                        STM32F4_SPI_CR1_BIDIOE);
+       } else if (comm_type == SPI_3WIRE_RX) {
+               stm32_spi_set_bits(spi, STM32F4_SPI_CR1,
+                                       STM32F4_SPI_CR1_BIDIMODE);
+               stm32_spi_clr_bits(spi, STM32F4_SPI_CR1,
+                                       STM32F4_SPI_CR1_BIDIOE);
        } else {
                return -EINVAL;
        }
@@ -1906,6 +1916,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
        master->prepare_message = stm32_spi_prepare_msg;
        master->transfer_one = stm32_spi_transfer_one;
        master->unprepare_message = stm32_spi_unprepare_msg;
+       master->flags = SPI_MASTER_MUST_TX;
 
        spi->dma_tx = dma_request_chan(spi->dev, "tx");
        if (IS_ERR(spi->dma_tx)) {
index ec7967be9e2f5622fe1e8091e89c10e6d9399a17..ecea15534c42e1b66fa4798a85c8d2e33e085136 100644 (file)
@@ -470,6 +470,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
 
        master->max_speed_hz = 100 * 1000 * 1000;
        master->min_speed_hz = 3 * 1000;
+       master->use_gpio_descriptors = true;
        master->set_cs = sun6i_spi_set_cs;
        master->transfer_one = sun6i_spi_transfer_one;
        master->num_chipselect = 4;
index 83edabdb41ade9641f4c3e30c9c91e3999553f8e..c2c58871a947ebe1dda8aa355a3c5e6f4d392af7 100644 (file)
@@ -1398,6 +1398,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
+               pm_runtime_put_noidle(&pdev->dev);
                goto exit_pm_disable;
        }
 
index 51442937920622b8339abd51117efaa81ba16348..02cf5f463ba676a378518646cc8ed64152a3c19c 100644 (file)
@@ -491,6 +491,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
+               pm_runtime_put_noidle(&pdev->dev);
                goto exit_pm_disable;
        }
 
index 7f4d932dade7b66fb113eb379ace0339e9304134..a07b72e9c344847e38099eff64565f51c6887b86 100644 (file)
@@ -1118,6 +1118,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
+               pm_runtime_put_noidle(&pdev->dev);
                goto exit_pm_disable;
        }
        tspi->def_command_reg  = SLINK_M_S;
index 0fa50979644d448768e94c6d8c94a9f28a872321..6a9ef8ee3cc9028f73c3bcf0a1f4ab3a8252373e 100644 (file)
@@ -659,8 +659,7 @@ static int uniphier_spi_probe(struct platform_device *pdev)
        priv->master = master;
        priv->is_save_param = false;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(priv->base)) {
                ret = PTR_ERR(priv->base);
                goto out_master_put;
@@ -716,8 +715,10 @@ static int uniphier_spi_probe(struct platform_device *pdev)
 
        master->dma_tx = dma_request_chan(&pdev->dev, "tx");
        if (IS_ERR_OR_NULL(master->dma_tx)) {
-               if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER)
+               if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
                        goto out_disable_clk;
+               }
                master->dma_tx = NULL;
                dma_tx_burst = INT_MAX;
        } else {
@@ -732,8 +733,10 @@ static int uniphier_spi_probe(struct platform_device *pdev)
 
        master->dma_rx = dma_request_chan(&pdev->dev, "rx");
        if (IS_ERR_OR_NULL(master->dma_rx)) {
-               if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER)
+               if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
                        goto out_disable_clk;
+               }
                master->dma_rx = NULL;
                dma_rx_burst = INT_MAX;
        } else {
index c92c89467e7ed5a688b6ac9ee15bb7f73bb82bcd..8158e281f35409ebbc19fa0cf04c539b5fd20e76 100644 (file)
@@ -1023,7 +1023,8 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
        void *tmp;
        unsigned int max_tx, max_rx;
 
-       if (ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX)) {
+       if ((ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX))
+               && !(msg->spi->mode & SPI_3WIRE)) {
                max_tx = 0;
                max_rx = 0;
 
@@ -1075,7 +1076,7 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
 {
        struct spi_statistics *statm = &ctlr->statistics;
        struct spi_statistics *stats = &msg->spi->statistics;
-       unsigned long long ms = 1;
+       unsigned long long ms;
 
        if (spi_controller_is_slave(ctlr)) {
                if (wait_for_completion_interruptible(&ctlr->xfer_completion)) {
@@ -1160,6 +1161,8 @@ int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer)
 {
        int delay;
 
+       might_sleep();
+
        if (!_delay)
                return -EINVAL;
 
@@ -2111,6 +2114,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
                        }
 
                        lookup->max_speed_hz = sb->connection_speed;
+                       lookup->bits_per_word = sb->data_bit_length;
 
                        if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
                                lookup->mode |= SPI_CPHA;
@@ -2760,6 +2764,8 @@ void spi_unregister_controller(struct spi_controller *ctlr)
        struct spi_controller *found;
        int id = ctlr->bus_num;
 
+       device_for_each_child(&ctlr->dev, NULL, __unregister);
+
        /* First make sure that this controller was ever added */
        mutex_lock(&board_lock);
        found = idr_find(&spi_master_idr, id);
@@ -2772,7 +2778,6 @@ void spi_unregister_controller(struct spi_controller *ctlr)
        list_del(&ctlr->list);
        mutex_unlock(&board_lock);
 
-       device_for_each_child(&ctlr->dev, NULL, __unregister);
        device_unregister(&ctlr->dev);
        /* free bus id */
        mutex_lock(&board_lock);
@@ -3853,8 +3858,7 @@ static u8 *buf;
  * is zero for success, else a negative errno status code.
  * This call may only be used from a context that may sleep.
  *
- * Parameters to this routine are always copied using a small buffer;
- * portable code should never use this for more than 32 bytes.
+ * Parameters to this routine are always copied using a small buffer.
  * Performance-sensitive or bulk transfer code should instead use
  * spi_{async,sync}() calls with dma-safe buffers.
  *
index 80dd1025b95304932ca5a8c6ca2c4fe468eb755b..d753df700e9ea3cc3373b952c996e876e9a26b24 100644 (file)
@@ -62,7 +62,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS);
 #define SPI_MODE_MASK          (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
                                | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
                                | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
-                               | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)
+                               | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
+                               | SPI_RX_QUAD | SPI_RX_OCTAL)
 
 struct spidev_data {
        dev_t                   devt;
index 473b465724f183b9226af0221016bd269677911c..0755b11348ed81bdff1f444897b0249c8793902d 100644 (file)
@@ -99,12 +99,12 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
 
 static int ion_heap_clear_pages(struct page **pages, int num, pgprot_t pgprot)
 {
-       void *addr = vm_map_ram(pages, num, -1, pgprot);
+       void *addr = vmap(pages, num, VM_MAP, pgprot);
 
        if (!addr)
                return -ENOMEM;
        memset(addr, 0, PAGE_SIZE * num);
-       vm_unmap_ram(addr, num);
+       vunmap(addr);
 
        return 0;
 }
index 68c5718be827bdec139fe1707339d469d8059b4b..0939f4a4c963b11a0ddd50d1d20f9682a3199147 100644 (file)
@@ -67,7 +67,6 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r)
                ((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) |
                ((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) |
                ((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) |
-               ((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) |
                ((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) |
                ((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) |
                ((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) |
@@ -411,6 +410,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
        struct gb_sdio_command_request request = {0};
        struct gb_sdio_command_response response;
        struct mmc_data *data = host->mrq->data;
+       unsigned int timeout_ms;
        u8 cmd_flags;
        u8 cmd_type;
        int i;
@@ -469,9 +469,12 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
                request.data_blksz = cpu_to_le16(data->blksz);
        }
 
-       ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND,
-                               &request, sizeof(request), &response,
-                               sizeof(response));
+       timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
+               GB_OPERATION_TIMEOUT_DEFAULT;
+
+       ret = gb_operation_sync_timeout(host->connection, GB_SDIO_TYPE_COMMAND,
+                                       &request, sizeof(request), &response,
+                                       sizeof(response), timeout_ms);
        if (ret < 0)
                goto out;
 
index f4a60b41401b988eaf18d2ec64da64df0101a994..a8ccd4f7032019349d31b5339869737993a483e3 100644 (file)
@@ -15,14 +15,12 @@ struct imgu_device;
  * @size:              size of the buffer in bytes.
  * @vaddr:             kernel virtual address.
  * @daddr:             iova dma address to access IPU3.
- * @vma:               private, a pointer to &struct vm_struct,
- *                     used for imgu_dmamap_free.
  */
 struct imgu_css_map {
        size_t size;
        void *vaddr;
        dma_addr_t daddr;
-       struct vm_struct *vma;
+       struct page **pages;
 };
 
 /**
index 7431322379f64f5f804719e6fcb3a3c223f58fd7..8a19b00241521ef31b7700a4e57b2eb6738250c2 100644 (file)
@@ -96,6 +96,7 @@ void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
        unsigned long shift = iova_shift(&imgu->iova_domain);
        struct device *dev = &imgu->pci_dev->dev;
        size_t size = PAGE_ALIGN(len);
+       int count = size >> PAGE_SHIFT;
        struct page **pages;
        dma_addr_t iovaddr;
        struct iova *iova;
@@ -114,7 +115,7 @@ void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
 
        /* Call IOMMU driver to setup pgt */
        iovaddr = iova_dma_addr(&imgu->iova_domain, iova);
-       for (i = 0; i < size / PAGE_SIZE; ++i) {
+       for (i = 0; i < count; ++i) {
                rval = imgu_mmu_map(imgu->mmu, iovaddr,
                                    page_to_phys(pages[i]), PAGE_SIZE);
                if (rval)
@@ -123,33 +124,23 @@ void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map,
                iovaddr += PAGE_SIZE;
        }
 
-       /* Now grab a virtual region */
-       map->vma = __get_vm_area(size, VM_USERMAP, VMALLOC_START, VMALLOC_END);
-       if (!map->vma)
+       map->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
+       if (!map->vaddr)
                goto out_unmap;
 
-       map->vma->pages = pages;
-       /* And map it in KVA */
-       if (map_vm_area(map->vma, PAGE_KERNEL, pages))
-               goto out_vunmap;
-
+       map->pages = pages;
        map->size = size;
        map->daddr = iova_dma_addr(&imgu->iova_domain, iova);
-       map->vaddr = map->vma->addr;
 
        dev_dbg(dev, "%s: allocated %zu @ IOVA %pad @ VA %p\n", __func__,
-               size, &map->daddr, map->vma->addr);
-
-       return map->vma->addr;
+               size, &map->daddr, map->vaddr);
 
-out_vunmap:
-       vunmap(map->vma->addr);
+       return map->vaddr;
 
 out_unmap:
        imgu_dmamap_free_buffer(pages, size);
        imgu_mmu_unmap(imgu->mmu, iova_dma_addr(&imgu->iova_domain, iova),
                       i * PAGE_SIZE);
-       map->vma = NULL;
 
 out_free_iova:
        __free_iova(&imgu->iova_domain, iova);
@@ -177,8 +168,6 @@ void imgu_dmamap_unmap(struct imgu_device *imgu, struct imgu_css_map *map)
  */
 void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map)
 {
-       struct vm_struct *area = map->vma;
-
        dev_dbg(&imgu->pci_dev->dev, "%s: freeing %zu @ IOVA %pad @ VA %p\n",
                __func__, map->size, &map->daddr, map->vaddr);
 
@@ -187,11 +176,8 @@ void imgu_dmamap_free(struct imgu_device *imgu, struct imgu_css_map *map)
 
        imgu_dmamap_unmap(imgu, map);
 
-       if (WARN_ON(!area) || WARN_ON(!area->pages))
-               return;
-
-       imgu_dmamap_free_buffer(area->pages, map->size);
        vunmap(map->vaddr);
+       imgu_dmamap_free_buffer(map->pages, map->size);
        map->vaddr = NULL;
 }
 
index 991f49ee402640e3bfd9e722595a797baa2d88f3..b09eac4b6d64f0ae01076a6f6d39164dc435a14b 100644 (file)
@@ -423,13 +423,6 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
        struct n_hdlc_buf *rbuf;
        DECLARE_WAITQUEUE(wait, current);
 
-       /* verify user access to buffer */
-       if (!access_ok(buf, nr)) {
-               pr_warn("%s(%d) %s() can't verify user buffer\n",
-                               __FILE__, __LINE__, __func__);
-               return -EFAULT;
-       }
-
        add_wait_queue(&tty->read_wait, &wait);
 
        for (;;) {
index 44f28a114c2b6bb43456b2c67a03b8ec02b7a389..94b6fa6e585e71653a6a4d2103ba50f2043f10b0 100644 (file)
@@ -598,8 +598,6 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
                return -EINVAL;
        if (nbytes <= 0)
                return 0;
-       if (!access_ok(buf, nbytes))
-               return -EFAULT;
 
        mutex_lock(&usb_bus_idr_lock);
        /* print devices for all busses */
index d93d94d7ff50605afddfb35afc360d5f2c88ff62..96d4507d988ac01949a9e89d1c4568d71b07ba17 100644 (file)
@@ -1136,11 +1136,6 @@ static int proc_control(struct usb_dev_state *ps, void __user *arg)
                ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
                ctrl.wIndex, ctrl.wLength);
        if (ctrl.bRequestType & 0x80) {
-               if (ctrl.wLength && !access_ok(ctrl.data,
-                                              ctrl.wLength)) {
-                       ret = -EINVAL;
-                       goto done;
-               }
                pipe = usb_rcvctrlpipe(dev, 0);
                snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0);
 
@@ -1225,10 +1220,6 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
        }
        tmo = bulk.timeout;
        if (bulk.ep & 0x80) {
-               if (len1 && !access_ok(bulk.data, len1)) {
-                       ret = -EINVAL;
-                       goto done;
-               }
                snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
 
                usb_unlock_device(dev);
index f3816a5c861eeeafdf1230afc1e7ca8fe41efa55..df671acdd464b7e9c323ebfbadc0fefd9e37acfd 100644 (file)
@@ -252,9 +252,6 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
        if (!count)
                return 0;
 
-       if (!access_ok(buffer, count))
-               return -EFAULT;
-
        spin_lock_irqsave(&hidg->read_spinlock, flags);
 
 #define READ_COND (!list_empty(&hidg->completed_out_req))
@@ -339,9 +336,6 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
        unsigned long flags;
        ssize_t status = -ENOMEM;
 
-       if (!access_ok(buffer, count))
-               return -EFAULT;
-
        spin_lock_irqsave(&hidg->write_spinlock, flags);
 
 #define WRITE_COND (!hidg->write_pending)
index 77eb97b2aa866c68902d5c7d64726fdcafe45715..a4dbd11f8ee265e252eb82af2ffe2046b93c92cc 100644 (file)
@@ -11,7 +11,7 @@ config TYPEC_MUX_PI3USB30532
 
 config TYPEC_MUX_INTEL_PMC
        tristate "Intel PMC mux control"
-       depends on INTEL_PMC_IPC
+       depends on INTEL_SCU_IPC
        select USB_ROLE_SWITCH
        help
          Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can
index c22e5c4bbf1a9e1bf25ed0fa310198b836b5b7c6..1ac0a3eb7dd8597669f3624b57ba85e38b333e8f 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/usb/typec_dp.h>
 #include <linux/usb/typec_tbt.h>
 
-#include <asm/intel_pmc_ipc.h>
+#include <asm/intel_scu_ipc.h>
 
 #define PMC_USBC_CMD           0xa7
 
@@ -97,6 +97,7 @@ struct pmc_usb_port {
 struct pmc_usb {
        u8 num_ports;
        struct device *dev;
+       struct intel_scu_ipc_dev *ipc;
        struct pmc_usb_port *port;
 };
 
@@ -108,9 +109,8 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
         * Error bit will always be 0 with the USBC command.
         * Status can be checked from the response message.
         */
-       intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len,
-                             (void *)response, 1);
-
+       intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, len,
+                                 response, sizeof(response));
        if (response[2]) {
                if (response[2] & BIT(1))
                        return -EIO;
@@ -374,6 +374,10 @@ static int pmc_usb_probe(struct platform_device *pdev)
        if (!pmc->port)
                return -ENOMEM;
 
+       pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
+       if (!pmc->ipc)
+               return -ENODEV;
+
        pmc->dev = &pdev->dev;
 
        /*
index 5b986d6c801d9413b6bd40381b55ae12c3b97bbb..fa3f39336246b9ad515d9c2535f0487e9fbb06f4 100644 (file)
@@ -41,8 +41,8 @@ config TYPEC_FUSB302
 config TYPEC_WCOVE
        tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
        depends on ACPI
+       depends on MFD_INTEL_PMC_BXT
        depends on INTEL_SOC_PMIC
-       depends on INTEL_PMC_IPC
        depends on BXT_WC_PMIC_OPREGION
        help
          This driver adds support for USB Type-C on Intel Broxton platforms
index 20e03e00b66d56393418577d3c610838a7463f9e..6062104f3afbab2ca6ae4720cb99afe5f979c2f3 100644 (file)
@@ -1855,8 +1855,6 @@ static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var,
        var->yspot = par->crsr.spot_y;
        if (size > var->height * var->width)
                return -ENAMETOOLONG;
-       if (!access_ok(data, size))
-               return -EFAULT;
        delta = 1 << par->crsr.fmode;
        lspr = lofsprite + (delta << 1);
        if (par->bplcon0 & BPC0_LACE)
@@ -1935,8 +1933,6 @@ static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var,
                return -EINVAL;
        if (!var->height)
                return -EINVAL;
-       if (!access_ok(data, var->width * var->height))
-               return -EFAULT;
        delta = 1 << fmode;
        lofsprite = shfsprite = (u_short *)spritememory;
        lspr = lofsprite + (delta << 1);
index 56995f44e76d173f6dc100d50614b20b74ea17af..f40be68d5aac975d3880e2b91a42d92276d55cc0 100644 (file)
@@ -482,9 +482,6 @@ static int omapfb_memory_read(struct fb_info *fbi,
        if (!display || !display->driver->memory_read)
                return -ENOENT;
 
-       if (!access_ok(mr->buffer, mr->buffer_size))
-               return -EFAULT;
-
        if (mr->w > 4096 || mr->h > 4096)
                return -EINVAL;
 
index 0663c604bd642c454e24a2988b8a12cdf4c347a6..b739c476955b313a0167d3f057bd306412207955 100644 (file)
@@ -1217,6 +1217,7 @@ config ITCO_WDT
        depends on (X86 || IA64) && PCI
        select WATCHDOG_CORE
        depends on I2C || I2C=n
+       depends on MFD_INTEL_PMC_BXT || !MFD_INTEL_PMC_BXT
        select LPC_ICH if !EXPERT
        select I2C_I801 if !EXPERT && I2C
        ---help---
index e707c4797f76e57d8e89eb9d5249728c409a58ad..a370a185a41c40811a8f0f3002b17f4678d91264 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/uaccess.h>             /* For copy_to_user/put_user/... */
 #include <linux/io.h>                  /* For inb/outb/... */
 #include <linux/platform_data/itco_wdt.h>
+#include <linux/mfd/intel_pmc_bxt.h>
 
 #include "iTCO_vendor.h"
 
@@ -233,12 +234,24 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
        return val != newval ? -EIO : 0;
 }
 
+static int update_no_reboot_bit_pmc(void *priv, bool set)
+{
+       struct intel_pmc_dev *pmc = priv;
+       u32 bits = PMC_CFG_NO_REBOOT_EN;
+       u32 value = set ? bits : 0;
+
+       return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
+}
+
 static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
-               struct itco_wdt_platform_data *pdata)
+                                        struct platform_device *pdev,
+                                        struct itco_wdt_platform_data *pdata)
 {
-       if (pdata->update_no_reboot_bit) {
-               p->update_no_reboot_bit = pdata->update_no_reboot_bit;
-               p->no_reboot_priv = pdata->no_reboot_priv;
+       if (pdata->no_reboot_use_pmc) {
+               struct intel_pmc_dev *pmc = dev_get_drvdata(pdev->dev.parent);
+
+               p->update_no_reboot_bit = update_no_reboot_bit_pmc;
+               p->no_reboot_priv = pmc;
                return;
        }
 
@@ -478,14 +491,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       iTCO_wdt_no_reboot_bit_setup(p, pdata);
+       iTCO_wdt_no_reboot_bit_setup(p, pdev, pdata);
 
        /*
         * Get the Memory-Mapped GCS or PMC register, we need it for the
         * NO_REBOOT flag (TCO v2 and v3).
         */
        if (p->iTCO_version >= 2 && p->iTCO_version < 6 &&
-           !pdata->update_no_reboot_bit) {
+           !pdata->no_reboot_use_pmc) {
                p->gcs_pmc_res = platform_get_resource(pdev,
                                                       IORESOURCE_MEM,
                                                       ICH_RES_MEM_GCS_PMC);
index 470213abfd3df65b6f863b664eb7a8aeb96cc2ae..1ae03b64ef8bf69752b2783c6a42da4d68768a87 100644 (file)
@@ -33,14 +33,24 @@ enum {
        SCU_WATCHDOG_KEEPALIVE,
 };
 
-static inline int wdt_command(int sub, u32 *in, int inlen)
+struct mid_wdt {
+       struct watchdog_device wd;
+       struct device *dev;
+       struct intel_scu_ipc_dev *scu;
+};
+
+static inline int
+wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
 {
-       return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
+       struct intel_scu_ipc_dev *scu = mid->scu;
+
+       return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
+                                                  inlen, size, NULL, 0);
 }
 
 static int wdt_start(struct watchdog_device *wd)
 {
-       struct device *dev = watchdog_get_drvdata(wd);
+       struct mid_wdt *mid = watchdog_get_drvdata(wd);
        int ret, in_size;
        int timeout = wd->timeout;
        struct ipc_wd_start {
@@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd)
        } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
 
        /*
-        * SCU expects the input size for watchdog IPC to
-        * be based on 4 bytes
+        * SCU expects the input size for watchdog IPC to be 2 which is the
+        * size of the structure in dwords. SCU IPC normally takes bytes
+        * but this is a special case where we specify size to be different
+        * than inlen.
         */
        in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
 
-       ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
+       ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
+                         sizeof(ipc_wd_start), in_size);
        if (ret)
-               dev_crit(dev, "error starting watchdog: %d\n", ret);
+               dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
 
        return ret;
 }
 
 static int wdt_ping(struct watchdog_device *wd)
 {
-       struct device *dev = watchdog_get_drvdata(wd);
+       struct mid_wdt *mid = watchdog_get_drvdata(wd);
        int ret;
 
-       ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
+       ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
        if (ret)
-               dev_crit(dev, "Error executing keepalive: %d\n", ret);
+               dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
 
        return ret;
 }
 
 static int wdt_stop(struct watchdog_device *wd)
 {
-       struct device *dev = watchdog_get_drvdata(wd);
+       struct mid_wdt *mid = watchdog_get_drvdata(wd);
        int ret;
 
-       ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
+       ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
        if (ret)
-               dev_crit(dev, "Error stopping watchdog: %d\n", ret);
+               dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
 
        return ret;
 }
@@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct watchdog_device *wdt_dev;
        struct intel_mid_wdt_pdata *pdata = dev->platform_data;
+       struct mid_wdt *mid;
        int ret;
 
        if (!pdata) {
@@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev)
                        return ret;
        }
 
-       wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL);
-       if (!wdt_dev)
+       mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
+       if (!mid)
                return -ENOMEM;
 
+       mid->dev = dev;
+       wdt_dev = &mid->wd;
+
        wdt_dev->info = &mid_wdt_info;
        wdt_dev->ops = &mid_wdt_ops;
        wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
@@ -135,7 +152,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
        wdt_dev->parent = dev;
 
        watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
-       watchdog_set_drvdata(wdt_dev, dev);
+       watchdog_set_drvdata(wdt_dev, mid);
 
        ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
                               IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
@@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
                return ret;
        }
 
+       mid->scu = devm_intel_scu_ipc_dev_get(dev);
+       if (!mid->scu)
+               return -EPROBE_DEFER;
+
        /*
         * The firmware followed by U-Boot leaves the watchdog running
         * with the default threshold which may vary. When we get here
index f08fbbfafd9a0344ac353179332f80cb0ac01f4c..d1ad3935fb85ec5e82183132bd3a6a36c79bd3a4 100644 (file)
@@ -166,7 +166,7 @@ config TMPFS
          space. If you unmount a tmpfs instance, everything stored therein is
          lost.
 
-         See <file:Documentation/filesystems/tmpfs.txt> for details.
+         See <file:Documentation/filesystems/tmpfs.rst> for details.
 
 config TMPFS_POSIX_ACL
        bool "Tmpfs POSIX Access Control Lists"
index 62dc4f577ba1330e0f373719b33692b97a5aad15..04f86b8c100e78018294b2cd57716a5cf3401a38 100644 (file)
@@ -36,6 +36,12 @@ config COMPAT_BINFMT_ELF
 config ARCH_BINFMT_ELF_STATE
        bool
 
+config ARCH_HAVE_ELF_PROT
+       bool
+
+config ARCH_USE_GNU_PROPERTY
+       bool
+
 config BINFMT_ELF_FDPIC
        bool "Kernel support for FDPIC ELF binaries"
        default y if !BINFMT_ELF
@@ -72,7 +78,7 @@ config CORE_DUMP_DEFAULT_ELF_HEADERS
 
          The core dump behavior can be controlled per process using
          the /proc/PID/coredump_filter pseudo-file; this setting is
-         inherited.  See Documentation/filesystems/proc.txt for details.
+         inherited.  See Documentation/filesystems/proc.rst for details.
 
          This config option changes the default setting of coredump_filter
          seen at boot time.  If unsure, say Y.
index df4650dccf6826f044c9a8d4d813c48cd06984e5..44738fed66251f40425fbebbac18ed904a8b9602 100644 (file)
@@ -12,7 +12,7 @@ config ADFS_FS
 
          The ADFS partition should be the first partition (i.e.,
          /dev/[hs]d?1) on each of your drives. Please read the file
-         <file:Documentation/filesystems/adfs.txt> for further details.
+         <file:Documentation/filesystems/adfs.rst> for further details.
 
          To compile this code as a module, choose M here: the module will be
          called adfs.
index 84c46b9025c5f2483339cfd6432bb4b855b44832..eb9d0ab850cb1d02fc1f704e6b13f2ff2ef80c0e 100644 (file)
@@ -9,7 +9,7 @@ config AFFS_FS
          FFS partition on your hard drive.  Amiga floppies however cannot be
          read with this driver due to an incompatibility of the floppy
          controller used in an Amiga and the standard floppy controller in
-         PCs and workstations. Read <file:Documentation/filesystems/affs.txt>
+         PCs and workstations. Read <file:Documentation/filesystems/affs.rst>
          and <file:fs/affs/Changes>.
 
          With this driver you can also mount disk files used by Bernd
index 3fb1f559e31716933c219c0f9e2a29c158dd1fdb..1ad211d72b3b4f15a5ffde80e55cca7a3639d5a7 100644 (file)
@@ -8,7 +8,7 @@ config AFS_FS
          If you say Y here, you will get an experimental Andrew File System
          driver. It currently only supports unsecured read-only AFS access.
 
-         See <file:Documentation/filesystems/afs.txt> for more information.
+         See <file:Documentation/filesystems/afs.rst> for more information.
 
          If unsure, say N.
 
@@ -18,7 +18,7 @@ config AFS_DEBUG
        help
          Say Y here to make runtime controllable debugging messages appear.
 
-         See <file:Documentation/filesystems/afs.txt> for more information.
+         See <file:Documentation/filesystems/afs.rst> for more information.
 
          If unsure, say N.
 
@@ -37,6 +37,6 @@ config AFS_DEBUG_CURSOR
          the dmesg log if the server rotation algorithm fails to successfully
          contact a server.
 
-         See <file:Documentation/filesystems/afs.txt> for more information.
+         See <file:Documentation/filesystems/afs.rst> for more information.
 
          If unsure, say N.
index 5f3d3d8149287777d1b051215c5279cb6bb89acb..6483f9274d5e9d7c685ec64e7627e5ca7ee06b3f 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -176,6 +176,7 @@ struct fsync_iocb {
        struct file             *file;
        struct work_struct      work;
        bool                    datasync;
+       struct cred             *creds;
 };
 
 struct poll_iocb {
@@ -1589,8 +1590,11 @@ static int aio_write(struct kiocb *req, const struct iocb *iocb,
 static void aio_fsync_work(struct work_struct *work)
 {
        struct aio_kiocb *iocb = container_of(work, struct aio_kiocb, fsync.work);
+       const struct cred *old_cred = override_creds(iocb->fsync.creds);
 
        iocb->ki_res.res = vfs_fsync(iocb->fsync.file, iocb->fsync.datasync);
+       revert_creds(old_cred);
+       put_cred(iocb->fsync.creds);
        iocb_put(iocb);
 }
 
@@ -1604,6 +1608,10 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb,
        if (unlikely(!req->file->f_op->fsync))
                return -EINVAL;
 
+       req->creds = prepare_creds();
+       if (!req->creds)
+               return -ENOMEM;
+
        req->datasync = datasync;
        INIT_WORK(&req->work, aio_fsync_work);
        schedule_work(&req->work);
index 3e1247f0791366b31100e750d31b6739b3f615b3..3a757805b585683a42dc62a0cf3fc0ad4193923d 100644 (file)
@@ -11,7 +11,7 @@ config BFS_FS
          on your /stand slice from within Linux.  You then also need to say Y
          to "UnixWare slices support", below.  More information about the BFS
          file system is contained in the file
-         <file:Documentation/filesystems/bfs.txt>.
+         <file:Documentation/filesystems/bfs.rst>.
 
          If you don't know what this is about, say N.
 
index 13f25e241ac46cbd2f5ffa23de45e60a035a0c1a..8945671fe0e53141d98dada9354eb01a07df1baf 100644 (file)
 #include <linux/sched/coredump.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/cputime.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
 #include <linux/cred.h>
 #include <linux/dax.h>
 #include <linux/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
 
+#ifndef ELF_COMPAT
+#define ELF_COMPAT 0
+#endif
+
 #ifndef user_long_t
 #define user_long_t long
 #endif
@@ -539,7 +545,8 @@ static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
 
 #endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */
 
-static inline int make_prot(u32 p_flags)
+static inline int make_prot(u32 p_flags, struct arch_elf_state *arch_state,
+                           bool has_interp, bool is_interp)
 {
        int prot = 0;
 
@@ -549,7 +556,8 @@ static inline int make_prot(u32 p_flags)
                prot |= PROT_WRITE;
        if (p_flags & PF_X)
                prot |= PROT_EXEC;
-       return prot;
+
+       return arch_elf_adjust_prot(prot, arch_state, has_interp, is_interp);
 }
 
 /* This is much more generalized than the library routine read function,
@@ -559,7 +567,8 @@ static inline int make_prot(u32 p_flags)
 
 static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
                struct file *interpreter,
-               unsigned long no_base, struct elf_phdr *interp_elf_phdata)
+               unsigned long no_base, struct elf_phdr *interp_elf_phdata,
+               struct arch_elf_state *arch_state)
 {
        struct elf_phdr *eppnt;
        unsigned long load_addr = 0;
@@ -591,7 +600,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
        for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
                if (eppnt->p_type == PT_LOAD) {
                        int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
-                       int elf_prot = make_prot(eppnt->p_flags);
+                       int elf_prot = make_prot(eppnt->p_flags, arch_state,
+                                                true, true);
                        unsigned long vaddr = 0;
                        unsigned long k, map_addr;
 
@@ -682,6 +692,111 @@ out:
  * libraries.  There is no binary dependent code anywhere else.
  */
 
+static int parse_elf_property(const char *data, size_t *off, size_t datasz,
+                             struct arch_elf_state *arch,
+                             bool have_prev_type, u32 *prev_type)
+{
+       size_t o, step;
+       const struct gnu_property *pr;
+       int ret;
+
+       if (*off == datasz)
+               return -ENOENT;
+
+       if (WARN_ON_ONCE(*off > datasz || *off % ELF_GNU_PROPERTY_ALIGN))
+               return -EIO;
+       o = *off;
+       datasz -= *off;
+
+       if (datasz < sizeof(*pr))
+               return -ENOEXEC;
+       pr = (const struct gnu_property *)(data + o);
+       o += sizeof(*pr);
+       datasz -= sizeof(*pr);
+
+       if (pr->pr_datasz > datasz)
+               return -ENOEXEC;
+
+       WARN_ON_ONCE(o % ELF_GNU_PROPERTY_ALIGN);
+       step = round_up(pr->pr_datasz, ELF_GNU_PROPERTY_ALIGN);
+       if (step > datasz)
+               return -ENOEXEC;
+
+       /* Properties are supposed to be unique and sorted on pr_type: */
+       if (have_prev_type && pr->pr_type <= *prev_type)
+               return -ENOEXEC;
+       *prev_type = pr->pr_type;
+
+       ret = arch_parse_elf_property(pr->pr_type, data + o,
+                                     pr->pr_datasz, ELF_COMPAT, arch);
+       if (ret)
+               return ret;
+
+       *off = o + step;
+       return 0;
+}
+
+#define NOTE_DATA_SZ SZ_1K
+#define GNU_PROPERTY_TYPE_0_NAME "GNU"
+#define NOTE_NAME_SZ (sizeof(GNU_PROPERTY_TYPE_0_NAME))
+
+static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
+                               struct arch_elf_state *arch)
+{
+       union {
+               struct elf_note nhdr;
+               char data[NOTE_DATA_SZ];
+       } note;
+       loff_t pos;
+       ssize_t n;
+       size_t off, datasz;
+       int ret;
+       bool have_prev_type;
+       u32 prev_type;
+
+       if (!IS_ENABLED(CONFIG_ARCH_USE_GNU_PROPERTY) || !phdr)
+               return 0;
+
+       /* load_elf_binary() shouldn't call us unless this is true... */
+       if (WARN_ON_ONCE(phdr->p_type != PT_GNU_PROPERTY))
+               return -ENOEXEC;
+
+       /* If the properties are crazy large, that's too bad (for now): */
+       if (phdr->p_filesz > sizeof(note))
+               return -ENOEXEC;
+
+       pos = phdr->p_offset;
+       n = kernel_read(f, &note, phdr->p_filesz, &pos);
+
+       BUILD_BUG_ON(sizeof(note) < sizeof(note.nhdr) + NOTE_NAME_SZ);
+       if (n < 0 || n < sizeof(note.nhdr) + NOTE_NAME_SZ)
+               return -EIO;
+
+       if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 ||
+           note.nhdr.n_namesz != NOTE_NAME_SZ ||
+           strncmp(note.data + sizeof(note.nhdr),
+                   GNU_PROPERTY_TYPE_0_NAME, n - sizeof(note.nhdr)))
+               return -ENOEXEC;
+
+       off = round_up(sizeof(note.nhdr) + NOTE_NAME_SZ,
+                      ELF_GNU_PROPERTY_ALIGN);
+       if (off > n)
+               return -ENOEXEC;
+
+       if (note.nhdr.n_descsz > n - off)
+               return -ENOEXEC;
+       datasz = off + note.nhdr.n_descsz;
+
+       have_prev_type = false;
+       do {
+               ret = parse_elf_property(note.data, &off, datasz, arch,
+                                        have_prev_type, &prev_type);
+               have_prev_type = true;
+       } while (!ret);
+
+       return ret == -ENOENT ? 0 : ret;
+}
+
 static int load_elf_binary(struct linux_binprm *bprm)
 {
        struct file *interpreter = NULL; /* to shut gcc up */
@@ -689,6 +804,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
        int load_addr_set = 0;
        unsigned long error;
        struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
+       struct elf_phdr *elf_property_phdata = NULL;
        unsigned long elf_bss, elf_brk;
        int bss_prot = 0;
        int retval, i;
@@ -726,6 +842,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
        for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
                char *elf_interpreter;
 
+               if (elf_ppnt->p_type == PT_GNU_PROPERTY) {
+                       elf_property_phdata = elf_ppnt;
+                       continue;
+               }
+
                if (elf_ppnt->p_type != PT_INTERP)
                        continue;
 
@@ -819,9 +940,14 @@ out_free_interp:
                        goto out_free_dentry;
 
                /* Pass PT_LOPROC..PT_HIPROC headers to arch code */
+               elf_property_phdata = NULL;
                elf_ppnt = interp_elf_phdata;
                for (i = 0; i < interp_elf_ex->e_phnum; i++, elf_ppnt++)
                        switch (elf_ppnt->p_type) {
+                       case PT_GNU_PROPERTY:
+                               elf_property_phdata = elf_ppnt;
+                               break;
+
                        case PT_LOPROC ... PT_HIPROC:
                                retval = arch_elf_pt_proc(interp_elf_ex,
                                                          elf_ppnt, interpreter,
@@ -832,6 +958,11 @@ out_free_interp:
                        }
        }
 
+       retval = parse_elf_properties(interpreter ?: bprm->file,
+                                     elf_property_phdata, &arch_state);
+       if (retval)
+               goto out_free_dentry;
+
        /*
         * Allow arch code to reject the ELF at this point, whilst it's
         * still possible to return an error to the code that invoked
@@ -913,7 +1044,8 @@ out_free_interp:
                        }
                }
 
-               elf_prot = make_prot(elf_ppnt->p_flags);
+               elf_prot = make_prot(elf_ppnt->p_flags, &arch_state,
+                                    !!interpreter, false);
 
                elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
 
@@ -1056,7 +1188,8 @@ out_free_interp:
        if (interpreter) {
                elf_entry = load_elf_interp(interp_elf_ex,
                                            interpreter,
-                                           load_bias, interp_elf_phdata);
+                                           load_bias, interp_elf_phdata,
+                                           &arch_state);
                if (!IS_ERR((void *)elf_entry)) {
                        /*
                         * load_elf_interp() returns relocation
@@ -1355,7 +1488,6 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
            vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
                u32 __user *header = (u32 __user *) vma->vm_start;
                u32 word;
-               mm_segment_t fs = get_fs();
                /*
                 * Doing it this way gets the constant folded by GCC.
                 */
@@ -1368,14 +1500,8 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
                magic.elfmag[EI_MAG1] = ELFMAG1;
                magic.elfmag[EI_MAG2] = ELFMAG2;
                magic.elfmag[EI_MAG3] = ELFMAG3;
-               /*
-                * Switch to the user "segment" for get_user(),
-                * then put back what elf_core_dump() had in place.
-                */
-               set_fs(USER_DS);
                if (unlikely(get_user(word, header)))
                        word = 0;
-               set_fs(fs);
                if (word == magic.cmp)
                        return PAGE_SIZE;
        }
@@ -1556,10 +1682,7 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)
 static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
                const kernel_siginfo_t *siginfo)
 {
-       mm_segment_t old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       copy_siginfo_to_user((user_siginfo_t __user *) csigdata, siginfo);
-       set_fs(old_fs);
+       copy_siginfo_to_external(csigdata, siginfo);
        fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata);
 }
 
@@ -1733,7 +1856,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
                    (!regset->active || regset->active(t->task, regset) > 0)) {
                        int ret;
                        size_t size = regset_size(t->task, regset);
-                       void *data = kmalloc(size, GFP_KERNEL);
+                       void *data = kzalloc(size, GFP_KERNEL);
                        if (unlikely(!data))
                                return 0;
                        ret = regset->get(t->task, regset,
@@ -2186,7 +2309,6 @@ static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
 static int elf_core_dump(struct coredump_params *cprm)
 {
        int has_dumped = 0;
-       mm_segment_t fs;
        int segs, i;
        size_t vma_data_size = 0;
        struct vm_area_struct *vma, *gate_vma;
@@ -2235,13 +2357,10 @@ static int elf_core_dump(struct coredump_params *cprm)
         * notes.  This also sets up the file header.
         */
        if (!fill_note_info(&elf, e_phnum, &info, cprm->siginfo, cprm->regs))
-               goto cleanup;
+               goto end_coredump;
 
        has_dumped = 1;
 
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-
        offset += sizeof(elf);                          /* Elf header */
        offset += segs * sizeof(struct elf_phdr);       /* Program headers */
 
@@ -2369,9 +2488,6 @@ static int elf_core_dump(struct coredump_params *cprm)
        }
 
 end_coredump:
-       set_fs(fs);
-
-cleanup:
        free_note_info(&info);
        kfree(shdr4extnum);
        kvfree(vma_filesz);
index 240f6666354377a8ea818df0299d8d1f5200fa52..d9501a86cec9713b63928c9b6cba5a60c9d3c1c0 100644 (file)
@@ -1549,7 +1549,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
 {
 #define        NUM_NOTES       6
        int has_dumped = 0;
-       mm_segment_t fs;
        int segs;
        int i;
        struct vm_area_struct *vma;
@@ -1589,31 +1588,31 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
        /* alloc memory for large data structures: too large to be on stack */
        elf = kmalloc(sizeof(*elf), GFP_KERNEL);
        if (!elf)
-               goto cleanup;
+               goto end_coredump;
        prstatus = kzalloc(sizeof(*prstatus), GFP_KERNEL);
        if (!prstatus)
-               goto cleanup;
+               goto end_coredump;
        psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
        if (!psinfo)
-               goto cleanup;
+               goto end_coredump;
        notes = kmalloc_array(NUM_NOTES, sizeof(struct memelfnote),
                              GFP_KERNEL);
        if (!notes)
-               goto cleanup;
+               goto end_coredump;
        fpu = kmalloc(sizeof(*fpu), GFP_KERNEL);
        if (!fpu)
-               goto cleanup;
+               goto end_coredump;
 #ifdef ELF_CORE_COPY_XFPREGS
        xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL);
        if (!xfpu)
-               goto cleanup;
+               goto end_coredump;
 #endif
 
        for (ct = current->mm->core_state->dumper.next;
                                        ct; ct = ct->next) {
                tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
                if (!tmp)
-                       goto cleanup;
+                       goto end_coredump;
 
                tmp->thread = ct->task;
                list_add(&tmp->list, &thread_list);
@@ -1678,9 +1677,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
                          "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu);
 #endif
 
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-
        offset += sizeof(*elf);                         /* Elf header */
        offset += segs * sizeof(struct elf_phdr);       /* Program headers */
 
@@ -1788,9 +1784,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
        }
 
 end_coredump:
-       set_fs(fs);
-
-cleanup:
        while (!list_empty(&thread_list)) {
                struct list_head *tmp = thread_list.next;
                list_del(tmp);
index 93672c3f1c78c886eaeffaff6746bf2ee2937d99..1e3030dd43ba8f45e7306f8455182b1fc1202618 100644 (file)
@@ -614,10 +614,9 @@ static int blkdev_readpage(struct file * file, struct page * page)
        return block_read_full_page(page, blkdev_get_block);
 }
 
-static int blkdev_readpages(struct file *file, struct address_space *mapping,
-                       struct list_head *pages, unsigned nr_pages)
+static void blkdev_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, blkdev_get_block);
+       mpage_readahead(rac, blkdev_get_block);
 }
 
 static int blkdev_write_begin(struct file *file, struct address_space *mapping,
@@ -2023,8 +2022,7 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
        if (bdev_read_only(I_BDEV(bd_inode)))
                return -EPERM;
 
-       /* uswsusp needs write permission to the swap */
-       if (IS_SWAPFILE(bd_inode) && !hibernation_available())
+       if (IS_SWAPFILE(bd_inode) && !is_hibernate_resume_dev(bd_inode))
                return -ETXTBSY;
 
        if (!iov_iter_count(from))
@@ -2085,7 +2083,7 @@ static int blkdev_writepages(struct address_space *mapping,
 
 static const struct address_space_operations def_blk_aops = {
        .readpage       = blkdev_readpage,
-       .readpages      = blkdev_readpages,
+       .readahead      = blkdev_readahead,
        .writepage      = blkdev_writepage,
        .write_begin    = blkdev_write_begin,
        .write_end      = blkdev_write_end,
index d10c7be10f3b80158201c81707c116e9b9ab9e42..7278789ff8a78f4ef918c44ea0d39eee00bb0d81 100644 (file)
@@ -980,9 +980,7 @@ static void btree_invalidatepage(struct page *page, unsigned int offset,
                btrfs_warn(BTRFS_I(page->mapping->host)->root->fs_info,
                           "page private not zero on page %llu",
                           (unsigned long long)page_offset(page));
-               ClearPagePrivate(page);
-               set_page_private(page, 0);
-               put_page(page);
+               detach_page_private(page);
        }
 }
 
index 39e45b8a503163ed07ad0fb31852ca37499d5b21..e12eb32d9e178c2caaa934094fbf3d38f94a4d2b 100644 (file)
@@ -3076,22 +3076,16 @@ static int submit_extent_page(unsigned int opf,
 static void attach_extent_buffer_page(struct extent_buffer *eb,
                                      struct page *page)
 {
-       if (!PagePrivate(page)) {
-               SetPagePrivate(page);
-               get_page(page);
-               set_page_private(page, (unsigned long)eb);
-       } else {
+       if (!PagePrivate(page))
+               attach_page_private(page, eb);
+       else
                WARN_ON(page->private != (unsigned long)eb);
-       }
 }
 
 void set_page_extent_mapped(struct page *page)
 {
-       if (!PagePrivate(page)) {
-               SetPagePrivate(page);
-               get_page(page);
-               set_page_private(page, EXTENT_PAGE_PRIVATE);
-       }
+       if (!PagePrivate(page))
+               attach_page_private(page, (void *)EXTENT_PAGE_PRIVATE);
 }
 
 static struct extent_map *
@@ -4367,51 +4361,32 @@ int extent_writepages(struct address_space *mapping,
        return ret;
 }
 
-int extent_readpages(struct address_space *mapping, struct list_head *pages,
-                    unsigned nr_pages)
+void extent_readahead(struct readahead_control *rac)
 {
        struct bio *bio = NULL;
        unsigned long bio_flags = 0;
        struct page *pagepool[16];
        struct extent_map *em_cached = NULL;
-       int nr = 0;
        u64 prev_em_start = (u64)-1;
+       int nr;
 
-       while (!list_empty(pages)) {
-               u64 contig_end = 0;
-
-               for (nr = 0; nr < ARRAY_SIZE(pagepool) && !list_empty(pages);) {
-                       struct page *page = lru_to_page(pages);
-
-                       prefetchw(&page->flags);
-                       list_del(&page->lru);
-                       if (add_to_page_cache_lru(page, mapping, page->index,
-                                               readahead_gfp_mask(mapping))) {
-                               put_page(page);
-                               break;
-                       }
-
-                       pagepool[nr++] = page;
-                       contig_end = page_offset(page) + PAGE_SIZE - 1;
-               }
+       while ((nr = readahead_page_batch(rac, pagepool))) {
+               u64 contig_start = page_offset(pagepool[0]);
+               u64 contig_end = page_offset(pagepool[nr - 1]) + PAGE_SIZE - 1;
 
-               if (nr) {
-                       u64 contig_start = page_offset(pagepool[0]);
+               ASSERT(contig_start + nr * PAGE_SIZE - 1 == contig_end);
 
-                       ASSERT(contig_start + nr * PAGE_SIZE - 1 == contig_end);
-
-                       contiguous_readpages(pagepool, nr, contig_start,
-                                    contig_end, &em_cached, &bio, &bio_flags,
-                                    &prev_em_start);
-               }
+               contiguous_readpages(pagepool, nr, contig_start, contig_end,
+                               &em_cached, &bio, &bio_flags, &prev_em_start);
        }
 
        if (em_cached)
                free_extent_map(em_cached);
 
-       if (bio)
-               return submit_one_bio(bio, 0, bio_flags);
-       return 0;
+       if (bio) {
+               if (submit_one_bio(bio, 0, bio_flags))
+                       return;
+       }
 }
 
 /*
@@ -4929,10 +4904,7 @@ static void btrfs_release_extent_buffer_pages(struct extent_buffer *eb)
                         * We need to make sure we haven't be attached
                         * to a new eb.
                         */
-                       ClearPagePrivate(page);
-                       set_page_private(page, 0);
-                       /* One for the page private */
-                       put_page(page);
+                       detach_page_private(page);
                }
 
                if (mapped)
index 2ed65bd0760ea901c13164ea1871bcbfbc446c0d..25594e09fdcde5f7268b064d3651888635ba0746 100644 (file)
@@ -198,8 +198,7 @@ int extent_writepages(struct address_space *mapping,
                      struct writeback_control *wbc);
 int btree_write_cache_pages(struct address_space *mapping,
                            struct writeback_control *wbc);
-int extent_readpages(struct address_space *mapping, struct list_head *pages,
-                    unsigned nr_pages);
+void extent_readahead(struct readahead_control *rac);
 int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                __u64 start, __u64 len);
 void set_page_extent_mapped(struct page *page);
index 320d1062068d36efa08869fd6f557255cf38b4cc..8b3489f229c79cb6b34a2dc242058bb9fb36f280 100644 (file)
@@ -4856,8 +4856,8 @@ static void evict_inode_truncate_pages(struct inode *inode)
 
        /*
         * Keep looping until we have no more ranges in the io tree.
-        * We can have ongoing bios started by readpages (called from readahead)
-        * that have their endio callback (extent_io.c:end_bio_extent_readpage)
+        * We can have ongoing bios started by readahead that have
+        * their endio callback (extent_io.c:end_bio_extent_readpage)
         * still in progress (unlocked the pages in the bio but did not yet
         * unlocked the ranges in the io tree). Therefore this means some
         * ranges can still be locked and eviction started because before
@@ -7050,11 +7050,11 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
                         * for it to complete) and then invalidate the pages for
                         * this range (through invalidate_inode_pages2_range()),
                         * but that can lead us to a deadlock with a concurrent
-                        * call to readpages() (a buffered read or a defrag call
+                        * call to readahead (a buffered read or a defrag call
                         * triggered a readahead) on a page lock due to an
                         * ordered dio extent we created before but did not have
                         * yet a corresponding bio submitted (whence it can not
-                        * complete), which makes readpages() wait for that
+                        * complete), which makes readahead wait for that
                         * ordered extent to complete while holding a lock on
                         * that page.
                         */
@@ -8293,21 +8293,16 @@ static int btrfs_writepages(struct address_space *mapping,
        return extent_writepages(mapping, wbc);
 }
 
-static int
-btrfs_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned nr_pages)
+static void btrfs_readahead(struct readahead_control *rac)
 {
-       return extent_readpages(mapping, pages, nr_pages);
+       extent_readahead(rac);
 }
 
 static int __btrfs_releasepage(struct page *page, gfp_t gfp_flags)
 {
        int ret = try_release_extent_mapping(page, gfp_flags);
-       if (ret == 1) {
-               ClearPagePrivate(page);
-               set_page_private(page, 0);
-               put_page(page);
-       }
+       if (ret == 1)
+               detach_page_private(page);
        return ret;
 }
 
@@ -8329,14 +8324,8 @@ static int btrfs_migratepage(struct address_space *mapping,
        if (ret != MIGRATEPAGE_SUCCESS)
                return ret;
 
-       if (page_has_private(page)) {
-               ClearPagePrivate(page);
-               get_page(newpage);
-               set_page_private(newpage, page_private(page));
-               set_page_private(page, 0);
-               put_page(page);
-               SetPagePrivate(newpage);
-       }
+       if (page_has_private(page))
+               attach_page_private(newpage, detach_page_private(page));
 
        if (PagePrivate2(page)) {
                ClearPagePrivate2(page);
@@ -8458,11 +8447,7 @@ again:
        }
 
        ClearPageChecked(page);
-       if (PagePrivate(page)) {
-               ClearPagePrivate(page);
-               set_page_private(page, 0);
-               put_page(page);
-       }
+       detach_page_private(page);
 }
 
 /*
@@ -10553,7 +10538,7 @@ static const struct address_space_operations btrfs_aops = {
        .readpage       = btrfs_readpage,
        .writepage      = btrfs_writepage,
        .writepages     = btrfs_writepages,
-       .readpages      = btrfs_readpages,
+       .readahead      = btrfs_readahead,
        .direct_IO      = btrfs_direct_IO,
        .invalidatepage = btrfs_invalidatepage,
        .releasepage    = btrfs_releasepage,
index c5f41bd86765f4452408e2e6b634dd02a2de3dd1..6a92ecf9eaa2777d48e897c075b44fc48fc613d1 100644 (file)
@@ -7065,13 +7065,6 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
                goto out;
        }
 
-       if (!access_ok(arg->clone_sources,
-                       sizeof(*arg->clone_sources) *
-                       arg->clone_sources_count)) {
-               ret = -EFAULT;
-               goto out;
-       }
-
        if (arg->flags & ~BTRFS_SEND_FLAG_MASK) {
                ret = -EINVAL;
                goto out;
index a60f60396cfa06efb622d2bea25261bf0ea0771a..64fe82ec65ff1fa7c4299f9c62a33b21a6bea8cf 100644 (file)
@@ -123,14 +123,6 @@ void __wait_on_buffer(struct buffer_head * bh)
 }
 EXPORT_SYMBOL(__wait_on_buffer);
 
-static void
-__clear_page_buffers(struct page *page)
-{
-       ClearPagePrivate(page);
-       set_page_private(page, 0);
-       put_page(page);
-}
-
 static void buffer_io_error(struct buffer_head *bh, char *msg)
 {
        if (!test_bit(BH_Quiet, &bh->b_state))
@@ -906,7 +898,7 @@ link_dev_buffers(struct page *page, struct buffer_head *head)
                bh = bh->b_this_page;
        } while (bh);
        tail->b_this_page = head;
-       attach_page_buffers(page, head);
+       attach_page_private(page, head);
 }
 
 static sector_t blkdev_max_block(struct block_device *bdev, unsigned int size)
@@ -1154,12 +1146,19 @@ EXPORT_SYMBOL(mark_buffer_dirty);
 
 void mark_buffer_write_io_error(struct buffer_head *bh)
 {
+       struct super_block *sb;
+
        set_buffer_write_io_error(bh);
        /* FIXME: do we need to set this in both places? */
        if (bh->b_page && bh->b_page->mapping)
                mapping_set_error(bh->b_page->mapping, -EIO);
        if (bh->b_assoc_map)
                mapping_set_error(bh->b_assoc_map, -EIO);
+       rcu_read_lock();
+       sb = READ_ONCE(bh->b_bdev->bd_super);
+       if (sb)
+               errseq_set(&sb->s_wb_err, -EIO);
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL(mark_buffer_write_io_error);
 
@@ -1580,7 +1579,7 @@ void create_empty_buffers(struct page *page,
                        bh = bh->b_this_page;
                } while (bh != head);
        }
-       attach_page_buffers(page, head);
+       attach_page_private(page, head);
        spin_unlock(&page->mapping->private_lock);
 }
 EXPORT_SYMBOL(create_empty_buffers);
@@ -2567,7 +2566,7 @@ static void attach_nobh_buffers(struct page *page, struct buffer_head *head)
                        bh->b_this_page = head;
                bh = bh->b_this_page;
        } while (bh != head);
-       attach_page_buffers(page, head);
+       attach_page_private(page, head);
        spin_unlock(&page->mapping->private_lock);
 }
 
@@ -3227,7 +3226,7 @@ drop_buffers(struct page *page, struct buffer_head **buffers_to_free)
                bh = next;
        } while (bh != head);
        *buffers_to_free = head;
-       __clear_page_buffers(page);
+       detach_page_private(page);
        return 1;
 failed:
        return 0;
index ae559ed5b3b33355afb0a01115a31942925d068e..ff9ca55a9ae9c83c6c5503e33fbff1d468409fb9 100644 (file)
@@ -8,7 +8,7 @@ config CACHEFILES
          filesystems - primarily networking filesystems - thus allowing fast
          local disk to enhance the speed of slower devices.
 
-         See Documentation/filesystems/caching/cachefiles.txt for more
+         See Documentation/filesystems/caching/cachefiles.rst for more
          information.
 
 config CACHEFILES_DEBUG
@@ -36,5 +36,5 @@ config CACHEFILES_HISTOGRAM
          bouncing between CPUs.  On the other hand, the histogram may be
          useful for debugging purposes.  Saying 'N' here is recommended.
 
-         See Documentation/filesystems/caching/cachefiles.txt for more
+         See Documentation/filesystems/caching/cachefiles.rst for more
          information.
index 5f3aa4d607def2fd104a8c2349f37555cb53cfc3..f1acde6fb9a6181985e878e981a977719c8c7f68 100644 (file)
@@ -3991,7 +3991,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
                        __ceph_queue_cap_release(session, cap);
                        spin_unlock(&session->s_cap_lock);
                }
-               goto done;
+               goto flush_cap_releases;
        }
 
        /* these will work even if we don't have a cap yet */
index c5e6eff5a38164e2dda4b20e1918c287233be1b3..ba0ded7842a7790769951357ba713146c2455640 100644 (file)
@@ -483,6 +483,9 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count)
        p->dev = dev;
        p->count = count;
 
+       if (WARN_ON(dev == WHITEOUT_DEV))
+               return -EBUSY;
+
        error = kobj_map(cdev_map, dev, count, NULL,
                         exact_match, exact_lock, p);
        if (error)
index ae6759f9594a226cdced8fc2441dae8f03bbcc96..c3477eeafb3f23a2b3f94c48f44e7d52abd5e707 100644 (file)
@@ -15,7 +15,7 @@ config CODA_FS
          *client*.  You will need user level code as well, both for the
          client and server.  Servers are currently user level, i.e. they need
          no kernel support.  Please read
-         <file:Documentation/filesystems/coda.txt> and check out the Coda
+         <file:Documentation/filesystems/coda.rst> and check out the Coda
          home page <http://www.coda.cs.cmu.edu/>.
 
          To compile the coda client support as a module, choose M here: the
index aaad4ca1217ef116494ea7315de081b0f3c36c4c..e61f3fe8e32a6a066565fa2a4633858f76a023a6 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/elfcore-compat.h>
 #include <linux/time.h>
 
+#define ELF_COMPAT     1
+
 /*
  * Rename the basic ELF layout types to refer to the 32-bit class of files.
  */
 #undef elf_shdr
 #undef elf_note
 #undef elf_addr_t
+#undef ELF_GNU_PROPERTY_ALIGN
 #define elfhdr         elf32_hdr
 #define elf_phdr       elf32_phdr
 #define elf_shdr       elf32_shdr
 #define elf_note       elf32_note
 #define elf_addr_t     Elf32_Addr
+#define ELF_GNU_PROPERTY_ALIGN ELF32_GNU_PROPERTY_ALIGN
 
 /*
  * Some data types as stored in coredump.
  */
 #define user_long_t            compat_long_t
 #define user_siginfo_t         compat_siginfo_t
-#define copy_siginfo_to_user   copy_siginfo_to_user32
+#define copy_siginfo_to_external       copy_siginfo_to_external32
 
 /*
  * The machine-dependent core note format types are defined in elfcore-compat.h,
index fd0b5dd68f9e4d94da0a59ecc274c76d696d9b62..8bd6a883c94c76c69177a12fa65fa46383c8adc3 100644 (file)
@@ -9,7 +9,7 @@
  *
  * configfs Copyright (C) 2005 Oracle.  All rights reserved.
  *
- * Please see Documentation/filesystems/configfs/configfs.txt for more
+ * Please see Documentation/filesystems/configfs.rst for more
  * information.
  */
 
index 6e0f1fcb8a5b7ff6f147131baef2219f1f1b787b..704a4356f1374f9328d7972e7a79ac26a73f6e6c 100644 (file)
@@ -9,7 +9,7 @@
  *
  * configfs Copyright (C) 2005 Oracle.  All rights reserved.
  *
- * Please see the file Documentation/filesystems/configfs/configfs.txt for
+ * Please see the file Documentation/filesystems/configfs.rst for
  * critical information about using the config_item interface.
  */
 
index c8bebb70a9716adc8c99b3c2cd5548d199938b02..d98cef0dbb6bbee405fc86eb3dbbc2ab8161ea8d 100644 (file)
@@ -9,7 +9,7 @@ config CRAMFS
          limited to 256MB file systems (with 16MB files), and doesn't support
          16/32 bits uid/gid, hard links and timestamps.
 
-         See <file:Documentation/filesystems/cramfs.txt> and
+         See <file:Documentation/filesystems/cramfs.rst> and
          <file:fs/cramfs/README> for further information.
 
          To compile this as a module, choose M here: the module will be called
index 1ecaac7ee3cb8e0ad958fd9b935eb4d4e0169f23..ed015cb66c7cb023e9ff215b71b9ea6967d14cd7 100644 (file)
@@ -54,6 +54,7 @@ struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags)
 
 /**
  * fscrypt_free_bounce_page() - free a ciphertext bounce page
+ * @bounce_page: the bounce page to free, or NULL
  *
  * Free a bounce page that was allocated by fscrypt_encrypt_pagecache_blocks(),
  * or by fscrypt_alloc_bounce_page() directly.
@@ -76,8 +77,12 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
        memset(iv, 0, ci->ci_mode->ivsize);
 
        if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
-               WARN_ON_ONCE((u32)lblk_num != lblk_num);
+               WARN_ON_ONCE(lblk_num > U32_MAX);
+               WARN_ON_ONCE(ci->ci_inode->i_ino > U32_MAX);
                lblk_num |= (u64)ci->ci_inode->i_ino << 32;
+       } else if (flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
+               WARN_ON_ONCE(lblk_num > U32_MAX);
+               lblk_num = (u32)(ci->ci_hashed_ino + lblk_num);
        } else if (flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
                memcpy(iv->nonce, ci->ci_nonce, FS_KEY_DERIVATION_NONCE_SIZE);
        }
@@ -132,7 +137,8 @@ int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
 }
 
 /**
- * fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a pagecache page
+ * fscrypt_encrypt_pagecache_blocks() - Encrypt filesystem blocks from a
+ *                                     pagecache page
  * @page:      The locked pagecache page containing the block(s) to encrypt
  * @len:       Total size of the block(s) to encrypt.  Must be a nonzero
  *             multiple of the filesystem's block size.
@@ -222,7 +228,8 @@ int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
 EXPORT_SYMBOL(fscrypt_encrypt_block_inplace);
 
 /**
- * fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a pagecache page
+ * fscrypt_decrypt_pagecache_blocks() - Decrypt filesystem blocks in a
+ *                                     pagecache page
  * @page:      The locked pagecache page containing the block(s) to decrypt
  * @len:       Total size of the block(s) to decrypt.  Must be a nonzero
  *             multiple of the filesystem's block size.
@@ -346,6 +353,8 @@ void fscrypt_msg(const struct inode *inode, const char *level,
 
 /**
  * fscrypt_init() - Set up for fs encryption.
+ *
+ * Return: 0 on success; -errno on failure
  */
 static int __init fscrypt_init(void)
 {
index 4c212442a8f7f1c939815893a50f69d68430929a..83ca5f1e7934b10a357e32e40311c9bb464e56c1 100644 (file)
@@ -18,7 +18,7 @@
 #include <crypto/skcipher.h>
 #include "fscrypt_private.h"
 
-/**
+/*
  * struct fscrypt_nokey_name - identifier for directory entry when key is absent
  *
  * When userspace lists an encrypted directory without access to the key, the
@@ -83,13 +83,8 @@ static int fscrypt_do_sha256(const u8 *data, unsigned int data_len, u8 *result)
                        tfm = prev_tfm;
                }
        }
-       {
-               SHASH_DESC_ON_STACK(desc, tfm);
 
-               desc->tfm = tfm;
-
-               return crypto_shash_digest(desc, data, data_len, result);
-       }
+       return crypto_shash_tfm_digest(tfm, data, data_len, result);
 }
 
 static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
@@ -105,9 +100,12 @@ static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
 
 /**
  * fscrypt_fname_encrypt() - encrypt a filename
- *
- * The output buffer must be at least as large as the input buffer.
- * Any extra space is filled with NUL padding before encryption.
+ * @inode: inode of the parent directory (for regular filenames)
+ *        or of the symlink (for symlink targets)
+ * @iname: the filename to encrypt
+ * @out: (output) the encrypted filename
+ * @olen: size of the encrypted filename.  It must be at least @iname->len.
+ *       Any extra space is filled with NUL padding before encryption.
  *
  * Return: 0 on success, -errno on failure
  */
@@ -157,8 +155,11 @@ int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
 
 /**
  * fname_decrypt() - decrypt a filename
- *
- * The caller must have allocated sufficient memory for the @oname string.
+ * @inode: inode of the parent directory (for regular filenames)
+ *        or of the symlink (for symlink targets)
+ * @iname: the encrypted filename to decrypt
+ * @oname: (output) the decrypted filename.  The caller must have allocated
+ *        enough space for this, e.g. using fscrypt_fname_alloc_buffer().
  *
  * Return: 0 on success, -errno on failure
  */
@@ -206,7 +207,10 @@ static const char lookup_table[65] =
 #define BASE64_CHARS(nbytes)   DIV_ROUND_UP((nbytes) * 4, 3)
 
 /**
- * base64_encode() -
+ * base64_encode() - base64-encode some bytes
+ * @src: the bytes to encode
+ * @len: number of bytes to encode
+ * @dst: (output) the base64-encoded string.  Not NUL-terminated.
  *
  * Encodes the input string using characters from the set [A-Za-z0-9+,].
  * The encoded string is roughly 4/3 times the size of the input string.
@@ -272,7 +276,12 @@ bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
 }
 
 /**
- * fscrypt_fname_alloc_buffer - allocate a buffer for presented filenames
+ * fscrypt_fname_alloc_buffer() - allocate a buffer for presented filenames
+ * @inode: inode of the parent directory (for regular filenames)
+ *        or of the symlink (for symlink targets)
+ * @max_encrypted_len: maximum length of encrypted filenames the buffer will be
+ *                    used to present
+ * @crypto_str: (output) buffer to allocate
  *
  * Allocate a buffer that is large enough to hold any decrypted or encoded
  * filename (null-terminated), for the given maximum encrypted filename length.
@@ -297,9 +306,10 @@ int fscrypt_fname_alloc_buffer(const struct inode *inode,
 EXPORT_SYMBOL(fscrypt_fname_alloc_buffer);
 
 /**
- * fscrypt_fname_free_buffer - free the buffer for presented filenames
+ * fscrypt_fname_free_buffer() - free a buffer for presented filenames
+ * @crypto_str: the buffer to free
  *
- * Free the buffer allocated by fscrypt_fname_alloc_buffer().
+ * Free a buffer that was allocated by fscrypt_fname_alloc_buffer().
  */
 void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
 {
@@ -311,10 +321,19 @@ void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str)
 EXPORT_SYMBOL(fscrypt_fname_free_buffer);
 
 /**
- * fscrypt_fname_disk_to_usr() - converts a filename from disk space to user
- * space
- *
- * The caller must have allocated sufficient memory for the @oname string.
+ * fscrypt_fname_disk_to_usr() - convert an encrypted filename to
+ *                              user-presentable form
+ * @inode: inode of the parent directory (for regular filenames)
+ *        or of the symlink (for symlink targets)
+ * @hash: first part of the name's dirhash, if applicable.  This only needs to
+ *       be provided if the filename is located in an indexed directory whose
+ *       encryption key may be unavailable.  Not needed for symlink targets.
+ * @minor_hash: second part of the name's dirhash, if applicable
+ * @iname: encrypted filename to convert.  May also be "." or "..", which
+ *        aren't actually encrypted.
+ * @oname: output buffer for the user-presentable filename.  The caller must
+ *        have allocated enough space for this, e.g. using
+ *        fscrypt_fname_alloc_buffer().
  *
  * If the key is available, we'll decrypt the disk name.  Otherwise, we'll
  * encode it for presentation in fscrypt_nokey_name format.
index dbced2937ec8954ae57a32cf2242de9abf819b00..eb7fcd2b7fb8fb42628ac9961c6e3efe69dc6e55 100644 (file)
@@ -43,7 +43,7 @@ struct fscrypt_context_v2 {
        u8 nonce[FS_KEY_DERIVATION_NONCE_SIZE];
 };
 
-/**
+/*
  * fscrypt_context - the encryption context of an inode
  *
  * This is the on-disk equivalent of an fscrypt_policy, stored alongside each
@@ -157,7 +157,7 @@ fscrypt_policy_flags(const union fscrypt_policy *policy)
        BUG();
 }
 
-/**
+/*
  * For encrypted symlinks, the ciphertext length is stored at the beginning
  * of the string in little-endian format.
  */
@@ -222,6 +222,9 @@ struct fscrypt_info {
 
        /* This inode's nonce, copied from the fscrypt_context */
        u8 ci_nonce[FS_KEY_DERIVATION_NONCE_SIZE];
+
+       /* Hashed inode number.  Only set for IV_INO_LBLK_32 */
+       u32 ci_hashed_ino;
 };
 
 typedef enum {
@@ -231,15 +234,14 @@ typedef enum {
 
 /* crypto.c */
 extern struct kmem_cache *fscrypt_info_cachep;
-extern int fscrypt_initialize(unsigned int cop_flags);
-extern int fscrypt_crypt_block(const struct inode *inode,
-                              fscrypt_direction_t rw, u64 lblk_num,
-                              struct page *src_page, struct page *dest_page,
-                              unsigned int len, unsigned int offs,
-                              gfp_t gfp_flags);
-extern struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags);
-
-extern void __printf(3, 4) __cold
+int fscrypt_initialize(unsigned int cop_flags);
+int fscrypt_crypt_block(const struct inode *inode, fscrypt_direction_t rw,
+                       u64 lblk_num, struct page *src_page,
+                       struct page *dest_page, unsigned int len,
+                       unsigned int offs, gfp_t gfp_flags);
+struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags);
+
+void __printf(3, 4) __cold
 fscrypt_msg(const struct inode *inode, const char *level, const char *fmt, ...);
 
 #define fscrypt_warn(inode, fmt, ...)          \
@@ -264,12 +266,10 @@ void fscrypt_generate_iv(union fscrypt_iv *iv, u64 lblk_num,
                         const struct fscrypt_info *ci);
 
 /* fname.c */
-extern int fscrypt_fname_encrypt(const struct inode *inode,
-                                const struct qstr *iname,
-                                u8 *out, unsigned int olen);
-extern bool fscrypt_fname_encrypted_size(const struct inode *inode,
-                                        u32 orig_len, u32 max_len,
-                                        u32 *encrypted_len_ret);
+int fscrypt_fname_encrypt(const struct inode *inode, const struct qstr *iname,
+                         u8 *out, unsigned int olen);
+bool fscrypt_fname_encrypted_size(const struct inode *inode, u32 orig_len,
+                                 u32 max_len, u32 *encrypted_len_ret);
 extern const struct dentry_operations fscrypt_d_ops;
 
 /* hkdf.c */
@@ -278,8 +278,8 @@ struct fscrypt_hkdf {
        struct crypto_shash *hmac_tfm;
 };
 
-extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
-                            unsigned int master_key_size);
+int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
+                     unsigned int master_key_size);
 
 /*
  * The list of contexts in which fscrypt uses HKDF.  These values are used as
@@ -293,12 +293,14 @@ extern int fscrypt_init_hkdf(struct fscrypt_hkdf *hkdf, const u8 *master_key,
 #define HKDF_CONTEXT_DIRECT_KEY                3
 #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY        4
 #define HKDF_CONTEXT_DIRHASH_KEY       5
+#define HKDF_CONTEXT_IV_INO_LBLK_32_KEY        6
+#define HKDF_CONTEXT_INODE_HASH_KEY    7
 
-extern int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
-                              const u8 *info, unsigned int infolen,
-                              u8 *okm, unsigned int okmlen);
+int fscrypt_hkdf_expand(const struct fscrypt_hkdf *hkdf, u8 context,
+                       const u8 *info, unsigned int infolen,
+                       u8 *okm, unsigned int okmlen);
 
-extern void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
+void fscrypt_destroy_hkdf(struct fscrypt_hkdf *hkdf);
 
 /* keyring.c */
 
@@ -389,14 +391,17 @@ struct fscrypt_master_key {
        struct list_head        mk_decrypted_inodes;
        spinlock_t              mk_decrypted_inodes_lock;
 
-       /* Crypto API transforms for DIRECT_KEY policies, allocated on-demand */
-       struct crypto_skcipher  *mk_direct_tfms[__FSCRYPT_MODE_MAX + 1];
-
        /*
-        * Crypto API transforms for filesystem-layer implementation of
-        * IV_INO_LBLK_64 policies, allocated on-demand.
+        * Per-mode encryption keys for the various types of encryption policies
+        * that use them.  Allocated and derived on-demand.
         */
-       struct crypto_skcipher  *mk_iv_ino_lblk_64_tfms[__FSCRYPT_MODE_MAX + 1];
+       struct crypto_skcipher *mk_direct_keys[__FSCRYPT_MODE_MAX + 1];
+       struct crypto_skcipher *mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1];
+       struct crypto_skcipher *mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1];
+
+       /* Hash key for inode numbers.  Initialized only when needed. */
+       siphash_key_t           mk_ino_hash_key;
+       bool                    mk_ino_hash_key_initialized;
 
 } __randomize_layout;
 
@@ -436,14 +441,17 @@ static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec)
        return 0;
 }
 
-extern struct key *
+struct key *
 fscrypt_find_master_key(struct super_block *sb,
                        const struct fscrypt_key_specifier *mk_spec);
 
-extern int fscrypt_verify_key_added(struct super_block *sb,
-                                   const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
+int fscrypt_add_test_dummy_key(struct super_block *sb,
+                              struct fscrypt_key_specifier *key_spec);
+
+int fscrypt_verify_key_added(struct super_block *sb,
+                            const u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]);
 
-extern int __init fscrypt_init_keyring(void);
+int __init fscrypt_init_keyring(void);
 
 /* keysetup.c */
 
@@ -457,33 +465,32 @@ struct fscrypt_mode {
 
 extern struct fscrypt_mode fscrypt_modes[];
 
-extern struct crypto_skcipher *
-fscrypt_allocate_skcipher(struct fscrypt_mode *mode, const u8 *raw_key,
-                         const struct inode *inode);
+struct crypto_skcipher *fscrypt_allocate_skcipher(struct fscrypt_mode *mode,
+                                                 const u8 *raw_key,
+                                                 const struct inode *inode);
 
-extern int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci,
-                                       const u8 *raw_key);
+int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);
 
-extern int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
-                                     const struct fscrypt_master_key *mk);
+int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
+                              const struct fscrypt_master_key *mk);
 
 /* keysetup_v1.c */
 
-extern void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+void fscrypt_put_direct_key(struct fscrypt_direct_key *dk);
+
+int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
+                             const u8 *raw_master_key);
 
-extern int fscrypt_setup_v1_file_key(struct fscrypt_info *ci,
-                                    const u8 *raw_master_key);
+int fscrypt_setup_v1_file_key_via_subscribed_keyrings(struct fscrypt_info *ci);
 
-extern int fscrypt_setup_v1_file_key_via_subscribed_keyrings(
-                                       struct fscrypt_info *ci);
 /* policy.c */
 
-extern bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
-                                  const union fscrypt_policy *policy2);
-extern bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
-                                    const struct inode *inode);
-extern int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
-                                      const union fscrypt_context *ctx_u,
-                                      int ctx_size);
+bool fscrypt_policies_equal(const union fscrypt_policy *policy1,
+                           const union fscrypt_policy *policy2);
+bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
+                             const struct inode *inode);
+int fscrypt_policy_from_context(union fscrypt_policy *policy_u,
+                               const union fscrypt_context *ctx_u,
+                               int ctx_size);
 
 #endif /* _FSCRYPT_PRIVATE_H */
index efb95bd19a8948f1fd7042a5f4a2e9ba2a967c2d..0cba7928446d34732886ff9cdb7b4c5c9bc81571 100644 (file)
@@ -44,17 +44,13 @@ static int hkdf_extract(struct crypto_shash *hmac_tfm, const u8 *ikm,
                        unsigned int ikmlen, u8 prk[HKDF_HASHLEN])
 {
        static const u8 default_salt[HKDF_HASHLEN];
-       SHASH_DESC_ON_STACK(desc, hmac_tfm);
        int err;
 
        err = crypto_shash_setkey(hmac_tfm, default_salt, HKDF_HASHLEN);
        if (err)
                return err;
 
-       desc->tfm = hmac_tfm;
-       err = crypto_shash_digest(desc, ikm, ikmlen, prk);
-       shash_desc_zero(desc);
-       return err;
+       return crypto_shash_tfm_digest(hmac_tfm, ikm, ikmlen, prk);
 }
 
 /*
index 5ef861742921c3b18c2f6d33edfea462ceb683ae..09fb8aa0f2e930ac65c5c23349ac1bac5a504029 100644 (file)
@@ -10,7 +10,7 @@
 #include "fscrypt_private.h"
 
 /**
- * fscrypt_file_open - prepare to open a possibly-encrypted regular file
+ * fscrypt_file_open() - prepare to open a possibly-encrypted regular file
  * @inode: the inode being opened
  * @filp: the struct file being set up
  *
@@ -262,7 +262,7 @@ err_free_sd:
 EXPORT_SYMBOL_GPL(__fscrypt_encrypt_symlink);
 
 /**
- * fscrypt_get_symlink - get the target of an encrypted symlink
+ * fscrypt_get_symlink() - get the target of an encrypted symlink
  * @inode: the symlink inode
  * @caddr: the on-disk contents of the symlink
  * @max_size: size of @caddr buffer
index ab41b25d4fa1ba3cc0390d33a5fa7d52a2c18dc2..e24eb48bfbe1f9143ebaff5715cbe230ab293e7d 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <crypto/skcipher.h>
 #include <linux/key-type.h>
+#include <linux/random.h>
 #include <linux/seq_file.h>
 
 #include "fscrypt_private.h"
@@ -44,8 +45,9 @@ static void free_master_key(struct fscrypt_master_key *mk)
        wipe_master_key_secret(&mk->mk_secret);
 
        for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) {
-               crypto_free_skcipher(mk->mk_direct_tfms[i]);
-               crypto_free_skcipher(mk->mk_iv_ino_lblk_64_tfms[i]);
+               crypto_free_skcipher(mk->mk_direct_keys[i]);
+               crypto_free_skcipher(mk->mk_iv_ino_lblk_64_keys[i]);
+               crypto_free_skcipher(mk->mk_iv_ino_lblk_32_keys[i]);
        }
 
        key_put(mk->mk_users);
@@ -424,9 +426,9 @@ static int add_existing_master_key(struct fscrypt_master_key *mk,
        return 0;
 }
 
-static int add_master_key(struct super_block *sb,
-                         struct fscrypt_master_key_secret *secret,
-                         const struct fscrypt_key_specifier *mk_spec)
+static int do_add_master_key(struct super_block *sb,
+                            struct fscrypt_master_key_secret *secret,
+                            const struct fscrypt_key_specifier *mk_spec)
 {
        static DEFINE_MUTEX(fscrypt_add_key_mutex);
        struct key *key;
@@ -465,6 +467,35 @@ out_unlock:
        return err;
 }
 
+static int add_master_key(struct super_block *sb,
+                         struct fscrypt_master_key_secret *secret,
+                         struct fscrypt_key_specifier *key_spec)
+{
+       int err;
+
+       if (key_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+               err = fscrypt_init_hkdf(&secret->hkdf, secret->raw,
+                                       secret->size);
+               if (err)
+                       return err;
+
+               /*
+                * Now that the HKDF context is initialized, the raw key is no
+                * longer needed.
+                */
+               memzero_explicit(secret->raw, secret->size);
+
+               /* Calculate the key identifier */
+               err = fscrypt_hkdf_expand(&secret->hkdf,
+                                         HKDF_CONTEXT_KEY_IDENTIFIER, NULL, 0,
+                                         key_spec->u.identifier,
+                                         FSCRYPT_KEY_IDENTIFIER_SIZE);
+               if (err)
+                       return err;
+       }
+       return do_add_master_key(sb, secret, key_spec);
+}
+
 static int fscrypt_provisioning_key_preparse(struct key_preparsed_payload *prep)
 {
        const struct fscrypt_provisioning_key_payload *payload = prep->data;
@@ -609,6 +640,15 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
        if (memchr_inv(arg.__reserved, 0, sizeof(arg.__reserved)))
                return -EINVAL;
 
+       /*
+        * Only root can add keys that are identified by an arbitrary descriptor
+        * rather than by a cryptographic hash --- since otherwise a malicious
+        * user could add the wrong key.
+        */
+       if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR &&
+           !capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
        memset(&secret, 0, sizeof(secret));
        if (arg.key_id) {
                if (arg.raw_size != 0)
@@ -626,54 +666,46 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
                        goto out_wipe_secret;
        }
 
-       switch (arg.key_spec.type) {
-       case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
-               /*
-                * Only root can add keys that are identified by an arbitrary
-                * descriptor rather than by a cryptographic hash --- since
-                * otherwise a malicious user could add the wrong key.
-                */
-               err = -EACCES;
-               if (!capable(CAP_SYS_ADMIN))
-                       goto out_wipe_secret;
-               break;
-       case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER:
-               err = fscrypt_init_hkdf(&secret.hkdf, secret.raw, secret.size);
-               if (err)
-                       goto out_wipe_secret;
-
-               /*
-                * Now that the HKDF context is initialized, the raw key is no
-                * longer needed.
-                */
-               memzero_explicit(secret.raw, secret.size);
-
-               /* Calculate the key identifier and return it to userspace. */
-               err = fscrypt_hkdf_expand(&secret.hkdf,
-                                         HKDF_CONTEXT_KEY_IDENTIFIER,
-                                         NULL, 0, arg.key_spec.u.identifier,
-                                         FSCRYPT_KEY_IDENTIFIER_SIZE);
-               if (err)
-                       goto out_wipe_secret;
-               err = -EFAULT;
-               if (copy_to_user(uarg->key_spec.u.identifier,
-                                arg.key_spec.u.identifier,
-                                FSCRYPT_KEY_IDENTIFIER_SIZE))
-                       goto out_wipe_secret;
-               break;
-       default:
-               WARN_ON(1);
-               err = -EINVAL;
+       err = add_master_key(sb, &secret, &arg.key_spec);
+       if (err)
                goto out_wipe_secret;
-       }
 
-       err = add_master_key(sb, &secret, &arg.key_spec);
+       /* Return the key identifier to userspace, if applicable */
+       err = -EFAULT;
+       if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER &&
+           copy_to_user(uarg->key_spec.u.identifier, arg.key_spec.u.identifier,
+                        FSCRYPT_KEY_IDENTIFIER_SIZE))
+               goto out_wipe_secret;
+       err = 0;
 out_wipe_secret:
        wipe_master_key_secret(&secret);
        return err;
 }
 EXPORT_SYMBOL_GPL(fscrypt_ioctl_add_key);
 
+/*
+ * Add the key for '-o test_dummy_encryption' to the filesystem keyring.
+ *
+ * Use a per-boot random key to prevent people from misusing this option.
+ */
+int fscrypt_add_test_dummy_key(struct super_block *sb,
+                              struct fscrypt_key_specifier *key_spec)
+{
+       static u8 test_key[FSCRYPT_MAX_KEY_SIZE];
+       struct fscrypt_master_key_secret secret;
+       int err;
+
+       get_random_once(test_key, FSCRYPT_MAX_KEY_SIZE);
+
+       memset(&secret, 0, sizeof(secret));
+       secret.size = FSCRYPT_MAX_KEY_SIZE;
+       memcpy(secret.raw, test_key, FSCRYPT_MAX_KEY_SIZE);
+
+       err = add_master_key(sb, &secret, key_spec);
+       wipe_master_key_secret(&secret);
+       return err;
+}
+
 /*
  * Verify that the current user has added a master key with the given identifier
  * (returns -ENOKEY if not).  This is needed to prevent a user from encrypting
index 302375e9f719ebbd7a1488390997687527707a57..1129adfa097ddfe36d7264c94a09899be7aa2014 100644 (file)
@@ -46,6 +46,8 @@ struct fscrypt_mode fscrypt_modes[] = {
        },
 };
 
+static DEFINE_MUTEX(fscrypt_mode_key_setup_mutex);
+
 static struct fscrypt_mode *
 select_encryption_mode(const union fscrypt_policy *policy,
                       const struct inode *inode)
@@ -130,7 +132,7 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
        const struct super_block *sb = inode->i_sb;
        struct fscrypt_mode *mode = ci->ci_mode;
        const u8 mode_num = mode - fscrypt_modes;
-       struct crypto_skcipher *tfm, *prev_tfm;
+       struct crypto_skcipher *tfm;
        u8 mode_key[FSCRYPT_MAX_KEY_SIZE];
        u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)];
        unsigned int hkdf_infolen = 0;
@@ -139,10 +141,17 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
        if (WARN_ON(mode_num > __FSCRYPT_MODE_MAX))
                return -EINVAL;
 
-       /* pairs with cmpxchg() below */
+       /* pairs with smp_store_release() below */
        tfm = READ_ONCE(tfms[mode_num]);
-       if (likely(tfm != NULL))
-               goto done;
+       if (likely(tfm != NULL)) {
+               ci->ci_ctfm = tfm;
+               return 0;
+       }
+
+       mutex_lock(&fscrypt_mode_key_setup_mutex);
+
+       if (tfms[mode_num])
+               goto done_unlock;
 
        BUILD_BUG_ON(sizeof(mode_num) != 1);
        BUILD_BUG_ON(sizeof(sb->s_uuid) != 16);
@@ -157,21 +166,21 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci,
                                  hkdf_context, hkdf_info, hkdf_infolen,
                                  mode_key, mode->keysize);
        if (err)
-               return err;
+               goto out_unlock;
        tfm = fscrypt_allocate_skcipher(mode, mode_key, inode);
        memzero_explicit(mode_key, mode->keysize);
-       if (IS_ERR(tfm))
-               return PTR_ERR(tfm);
-
-       /* pairs with READ_ONCE() above */
-       prev_tfm = cmpxchg(&tfms[mode_num], NULL, tfm);
-       if (prev_tfm != NULL) {
-               crypto_free_skcipher(tfm);
-               tfm = prev_tfm;
+       if (IS_ERR(tfm)) {
+               err = PTR_ERR(tfm);
+               goto out_unlock;
        }
-done:
+       /* pairs with READ_ONCE() above */
+       smp_store_release(&tfms[mode_num], tfm);
+done_unlock:
        ci->ci_ctfm = tfm;
-       return 0;
+       err = 0;
+out_unlock:
+       mutex_unlock(&fscrypt_mode_key_setup_mutex);
+       return err;
 }
 
 int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
@@ -189,6 +198,43 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci,
        return 0;
 }
 
+static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_info *ci,
+                                           struct fscrypt_master_key *mk)
+{
+       int err;
+
+       err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys,
+                                    HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true);
+       if (err)
+               return err;
+
+       /* pairs with smp_store_release() below */
+       if (!smp_load_acquire(&mk->mk_ino_hash_key_initialized)) {
+
+               mutex_lock(&fscrypt_mode_key_setup_mutex);
+
+               if (mk->mk_ino_hash_key_initialized)
+                       goto unlock;
+
+               err = fscrypt_hkdf_expand(&mk->mk_secret.hkdf,
+                                         HKDF_CONTEXT_INODE_HASH_KEY, NULL, 0,
+                                         (u8 *)&mk->mk_ino_hash_key,
+                                         sizeof(mk->mk_ino_hash_key));
+               if (err)
+                       goto unlock;
+               /* pairs with smp_load_acquire() above */
+               smp_store_release(&mk->mk_ino_hash_key_initialized, true);
+unlock:
+               mutex_unlock(&fscrypt_mode_key_setup_mutex);
+               if (err)
+                       return err;
+       }
+
+       ci->ci_hashed_ino = (u32)siphash_1u64(ci->ci_inode->i_ino,
+                                             &mk->mk_ino_hash_key);
+       return 0;
+}
+
 static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
                                     struct fscrypt_master_key *mk)
 {
@@ -203,7 +249,7 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
                 * encryption key.  This ensures that the master key is
                 * consistently used only for HKDF, avoiding key reuse issues.
                 */
-               err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_tfms,
+               err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys,
                                             HKDF_CONTEXT_DIRECT_KEY, false);
        } else if (ci->ci_policy.v2.flags &
                   FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) {
@@ -211,11 +257,14 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci,
                 * IV_INO_LBLK_64: encryption keys are derived from (master_key,
                 * mode_num, filesystem_uuid), and inode number is included in
                 * the IVs.  This format is optimized for use with inline
-                * encryption hardware compliant with the UFS or eMMC standards.
+                * encryption hardware compliant with the UFS standard.
                 */
-               err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_tfms,
+               err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys,
                                             HKDF_CONTEXT_IV_INO_LBLK_64_KEY,
                                             true);
+       } else if (ci->ci_policy.v2.flags &
+                  FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) {
+               err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk);
        } else {
                u8 derived_key[FSCRYPT_MAX_KEY_SIZE];
 
@@ -395,21 +444,18 @@ int fscrypt_get_encryption_info(struct inode *inode)
 
        res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
        if (res < 0) {
-               if (!fscrypt_dummy_context_enabled(inode) ||
-                   IS_ENCRYPTED(inode)) {
+               const union fscrypt_context *dummy_ctx =
+                       fscrypt_get_dummy_context(inode->i_sb);
+
+               if (IS_ENCRYPTED(inode) || !dummy_ctx) {
                        fscrypt_warn(inode,
                                     "Error %d getting encryption context",
                                     res);
                        return res;
                }
                /* Fake up a context for an unencrypted directory */
-               memset(&ctx, 0, sizeof(ctx));
-               ctx.version = FSCRYPT_CONTEXT_V1;
-               ctx.v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
-               ctx.v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
-               memset(ctx.v1.master_key_descriptor, 0x42,
-                      FSCRYPT_KEY_DESCRIPTOR_SIZE);
-               res = sizeof(ctx.v1);
+               res = fscrypt_context_size(dummy_ctx);
+               memcpy(&ctx, dummy_ctx, res);
        }
 
        crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
@@ -475,7 +521,8 @@ out:
 EXPORT_SYMBOL(fscrypt_get_encryption_info);
 
 /**
- * fscrypt_put_encryption_info - free most of an inode's fscrypt data
+ * fscrypt_put_encryption_info() - free most of an inode's fscrypt data
+ * @inode: an inode being evicted
  *
  * Free the inode's fscrypt_info.  Filesystems must call this when the inode is
  * being evicted.  An RCU grace period need not have elapsed yet.
@@ -488,7 +535,8 @@ void fscrypt_put_encryption_info(struct inode *inode)
 EXPORT_SYMBOL(fscrypt_put_encryption_info);
 
 /**
- * fscrypt_free_inode - free an inode's fscrypt data requiring RCU delay
+ * fscrypt_free_inode() - free an inode's fscrypt data requiring RCU delay
+ * @inode: an inode being freed
  *
  * Free the inode's cached decrypted symlink target, if any.  Filesystems must
  * call this after an RCU grace period, just before they free the inode.
@@ -503,7 +551,8 @@ void fscrypt_free_inode(struct inode *inode)
 EXPORT_SYMBOL(fscrypt_free_inode);
 
 /**
- * fscrypt_drop_inode - check whether the inode's master key has been removed
+ * fscrypt_drop_inode() - check whether the inode's master key has been removed
+ * @inode: an inode being considered for eviction
  *
  * Filesystems supporting fscrypt must call this from their ->drop_inode()
  * method so that encrypted inodes are evicted as soon as they're no longer in
index 10ccf945020ce5e1048dae45959072e937fc2f59..d23ff162c78bce9e72b165f86fe10e8bb652477a 100644 (file)
  */
 
 #include <linux/random.h>
+#include <linux/seq_file.h>
 #include <linux/string.h>
 #include <linux/mount.h>
 #include "fscrypt_private.h"
 
 /**
- * fscrypt_policies_equal - check whether two encryption policies are the same
+ * fscrypt_policies_equal() - check whether two encryption policies are the same
+ * @policy1: the first policy
+ * @policy2: the second policy
  *
  * Return: %true if equal, else %false
  */
@@ -66,18 +69,14 @@ static bool supported_direct_key_modes(const struct inode *inode,
        return true;
 }
 
-static bool supported_iv_ino_lblk_64_policy(
-                                       const struct fscrypt_policy_v2 *policy,
-                                       const struct inode *inode)
+static bool supported_iv_ino_lblk_policy(const struct fscrypt_policy_v2 *policy,
+                                        const struct inode *inode,
+                                        const char *type,
+                                        int max_ino_bits, int max_lblk_bits)
 {
        struct super_block *sb = inode->i_sb;
        int ino_bits = 64, lblk_bits = 64;
 
-       if (policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) {
-               fscrypt_warn(inode,
-                            "The DIRECT_KEY and IV_INO_LBLK_64 flags are mutually exclusive");
-               return false;
-       }
        /*
         * It's unsafe to include inode numbers in the IVs if the filesystem can
         * potentially renumber inodes, e.g. via filesystem shrinking.
@@ -85,16 +84,22 @@ static bool supported_iv_ino_lblk_64_policy(
        if (!sb->s_cop->has_stable_inodes ||
            !sb->s_cop->has_stable_inodes(sb)) {
                fscrypt_warn(inode,
-                            "Can't use IV_INO_LBLK_64 policy on filesystem '%s' because it doesn't have stable inode numbers",
-                            sb->s_id);
+                            "Can't use %s policy on filesystem '%s' because it doesn't have stable inode numbers",
+                            type, sb->s_id);
                return false;
        }
        if (sb->s_cop->get_ino_and_lblk_bits)
                sb->s_cop->get_ino_and_lblk_bits(sb, &ino_bits, &lblk_bits);
-       if (ino_bits > 32 || lblk_bits > 32) {
+       if (ino_bits > max_ino_bits) {
+               fscrypt_warn(inode,
+                            "Can't use %s policy on filesystem '%s' because its inode numbers are too long",
+                            type, sb->s_id);
+               return false;
+       }
+       if (lblk_bits > max_lblk_bits) {
                fscrypt_warn(inode,
-                            "Can't use IV_INO_LBLK_64 policy on filesystem '%s' because it doesn't use 32-bit inode and block numbers",
-                            sb->s_id);
+                            "Can't use %s policy on filesystem '%s' because its block numbers are too long",
+                            type, sb->s_id);
                return false;
        }
        return true;
@@ -137,6 +142,8 @@ static bool fscrypt_supported_v1_policy(const struct fscrypt_policy_v1 *policy,
 static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
                                        const struct inode *inode)
 {
+       int count = 0;
+
        if (!fscrypt_valid_enc_modes(policy->contents_encryption_mode,
                                     policy->filenames_encryption_mode)) {
                fscrypt_warn(inode,
@@ -152,13 +159,29 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
                return false;
        }
 
+       count += !!(policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY);
+       count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64);
+       count += !!(policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32);
+       if (count > 1) {
+               fscrypt_warn(inode, "Mutually exclusive encryption flags (0x%02x)",
+                            policy->flags);
+               return false;
+       }
+
        if ((policy->flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY) &&
            !supported_direct_key_modes(inode, policy->contents_encryption_mode,
                                        policy->filenames_encryption_mode))
                return false;
 
        if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) &&
-           !supported_iv_ino_lblk_64_policy(policy, inode))
+           !supported_iv_ino_lblk_policy(policy, inode, "IV_INO_LBLK_64",
+                                         32, 32))
+               return false;
+
+       if ((policy->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
+           /* This uses hashed inode numbers, so ino_bits doesn't matter. */
+           !supported_iv_ino_lblk_policy(policy, inode, "IV_INO_LBLK_32",
+                                         INT_MAX, 32))
                return false;
 
        if (memchr_inv(policy->__reserved, 0, sizeof(policy->__reserved))) {
@@ -170,7 +193,9 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy,
 }
 
 /**
- * fscrypt_supported_policy - check whether an encryption policy is supported
+ * fscrypt_supported_policy() - check whether an encryption policy is supported
+ * @policy_u: the encryption policy
+ * @inode: the inode on which the policy will be used
  *
  * Given an encryption policy, check whether all its encryption modes and other
  * settings are supported by this kernel on the given inode.  (But we don't
@@ -192,7 +217,10 @@ bool fscrypt_supported_policy(const union fscrypt_policy *policy_u,
 }
 
 /**
- * fscrypt_new_context_from_policy - create a new fscrypt_context from a policy
+ * fscrypt_new_context_from_policy() - create a new fscrypt_context from
+ *                                    an fscrypt_policy
+ * @ctx_u: output context
+ * @policy_u: input policy
  *
  * Create an fscrypt_context for an inode that is being assigned the given
  * encryption policy.  A new nonce is randomly generated.
@@ -242,7 +270,11 @@ static int fscrypt_new_context_from_policy(union fscrypt_context *ctx_u,
 }
 
 /**
- * fscrypt_policy_from_context - convert an fscrypt_context to an fscrypt_policy
+ * fscrypt_policy_from_context() - convert an fscrypt_context to
+ *                                an fscrypt_policy
+ * @policy_u: output policy
+ * @ctx_u: input context
+ * @ctx_size: size of input context in bytes
  *
  * Given an fscrypt_context, build the corresponding fscrypt_policy.
  *
@@ -354,6 +386,9 @@ static int set_encryption_policy(struct inode *inode,
                                               policy->v2.master_key_identifier);
                if (err)
                        return err;
+               if (policy->v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)
+                       pr_warn_once("%s (pid %d) is setting an IV_INO_LBLK_32 encryption policy.  This should only be used if there are certain hardware limitations.\n",
+                                    current->comm, current->pid);
                break;
        default:
                WARN_ON(1);
@@ -605,3 +640,127 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
        return preload ? fscrypt_get_encryption_info(child): 0;
 }
 EXPORT_SYMBOL(fscrypt_inherit_context);
+
+/**
+ * fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption'
+ * @sb: the filesystem on which test_dummy_encryption is being specified
+ * @arg: the argument to the test_dummy_encryption option.
+ *      If no argument was specified, then @arg->from == NULL.
+ * @dummy_ctx: the filesystem's current dummy context (input/output, see below)
+ *
+ * Handle the test_dummy_encryption mount option by creating a dummy encryption
+ * context, saving it in @dummy_ctx, and adding the corresponding dummy
+ * encryption key to the filesystem.  If the @dummy_ctx is already set, then
+ * instead validate that it matches @arg.  Don't support changing it via
+ * remount, as that is difficult to do safely.
+ *
+ * The reason we use an fscrypt_context rather than an fscrypt_policy is because
+ * we mustn't generate a new nonce each time we access a dummy-encrypted
+ * directory, as that would change the way filenames are encrypted.
+ *
+ * Return: 0 on success (dummy context set, or the same context is already set);
+ *         -EEXIST if a different dummy context is already set;
+ *         or another -errno value.
+ */
+int fscrypt_set_test_dummy_encryption(struct super_block *sb,
+                                     const substring_t *arg,
+                                     struct fscrypt_dummy_context *dummy_ctx)
+{
+       const char *argstr = "v2";
+       const char *argstr_to_free = NULL;
+       struct fscrypt_key_specifier key_spec = { 0 };
+       int version;
+       union fscrypt_context *ctx = NULL;
+       int err;
+
+       if (arg->from) {
+               argstr = argstr_to_free = match_strdup(arg);
+               if (!argstr)
+                       return -ENOMEM;
+       }
+
+       if (!strcmp(argstr, "v1")) {
+               version = FSCRYPT_CONTEXT_V1;
+               key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
+               memset(key_spec.u.descriptor, 0x42,
+                      FSCRYPT_KEY_DESCRIPTOR_SIZE);
+       } else if (!strcmp(argstr, "v2")) {
+               version = FSCRYPT_CONTEXT_V2;
+               key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+               /* key_spec.u.identifier gets filled in when adding the key */
+       } else {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (dummy_ctx->ctx) {
+               /*
+                * Note: if we ever make test_dummy_encryption support
+                * specifying other encryption settings, such as the encryption
+                * modes, we'll need to compare those settings here.
+                */
+               if (dummy_ctx->ctx->version == version)
+                       err = 0;
+               else
+                       err = -EEXIST;
+               goto out;
+       }
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = fscrypt_add_test_dummy_key(sb, &key_spec);
+       if (err)
+               goto out;
+
+       ctx->version = version;
+       switch (ctx->version) {
+       case FSCRYPT_CONTEXT_V1:
+               ctx->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
+               ctx->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
+               memcpy(ctx->v1.master_key_descriptor, key_spec.u.descriptor,
+                      FSCRYPT_KEY_DESCRIPTOR_SIZE);
+               break;
+       case FSCRYPT_CONTEXT_V2:
+               ctx->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
+               ctx->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
+               memcpy(ctx->v2.master_key_identifier, key_spec.u.identifier,
+                      FSCRYPT_KEY_IDENTIFIER_SIZE);
+               break;
+       default:
+               WARN_ON(1);
+               err = -EINVAL;
+               goto out;
+       }
+       dummy_ctx->ctx = ctx;
+       ctx = NULL;
+       err = 0;
+out:
+       kfree(ctx);
+       kfree(argstr_to_free);
+       return err;
+}
+EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
+
+/**
+ * fscrypt_show_test_dummy_encryption() - show '-o test_dummy_encryption'
+ * @seq: the seq_file to print the option to
+ * @sep: the separator character to use
+ * @sb: the filesystem whose options are being shown
+ *
+ * Show the test_dummy_encryption mount option, if it was specified.
+ * This is mainly used for /proc/mounts.
+ */
+void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
+                                       struct super_block *sb)
+{
+       const union fscrypt_context *ctx = fscrypt_get_dummy_context(sb);
+
+       if (!ctx)
+               return;
+       seq_printf(seq, "%ctest_dummy_encryption=v%d", sep, ctx->version);
+}
+EXPORT_SYMBOL_GPL(fscrypt_show_test_dummy_encryption);
index 522c35d5292bf5a1690ac288280acba60a82f5c6..1bdeaa6d57900cccab5aa099766f791033243c74 100644 (file)
@@ -7,7 +7,7 @@ config ECRYPT_FS
        select CRYPTO_MD5
        help
          Encrypted filesystem that operates on the VFS layer.  See
-         <file:Documentation/filesystems/ecryptfs.txt> to learn more about
+         <file:Documentation/filesystems/ecryptfs.rst> to learn more about
          eCryptfs.  Userspace components are required and can be
          obtained from <http://ecryptfs.sf.net>.
 
index 2c449aed1b9209e708c049accbab3050604eb100..0681540c48d98581715a143a4cc4ff85ef2cca99 100644 (file)
@@ -48,18 +48,6 @@ void ecryptfs_from_hex(char *dst, char *src, int dst_size)
        }
 }
 
-static int ecryptfs_hash_digest(struct crypto_shash *tfm,
-                               char *src, int len, char *dst)
-{
-       SHASH_DESC_ON_STACK(desc, tfm);
-       int err;
-
-       desc->tfm = tfm;
-       err = crypto_shash_digest(desc, src, len, dst);
-       shash_desc_zero(desc);
-       return err;
-}
-
 /**
  * ecryptfs_calculate_md5 - calculates the md5 of @src
  * @dst: Pointer to 16 bytes of allocated memory
@@ -74,11 +62,8 @@ static int ecryptfs_calculate_md5(char *dst,
                                  struct ecryptfs_crypt_stat *crypt_stat,
                                  char *src, int len)
 {
-       struct crypto_shash *tfm;
-       int rc = 0;
+       int rc = crypto_shash_tfm_digest(crypt_stat->hash_tfm, src, len, dst);
 
-       tfm = crypt_stat->hash_tfm;
-       rc = ecryptfs_hash_digest(tfm, src, len, dst);
        if (rc) {
                printk(KERN_ERR
                       "%s: Error computing crypto hash; rc = [%d]\n",
index fc3a8d8064f84f8ee1ce24787c2632ca17c09650..d0542151e8c4acab509e60d30d3405bd05f9122e 100644 (file)
@@ -280,47 +280,36 @@ static int erofs_raw_access_readpage(struct file *file, struct page *page)
        return 0;
 }
 
-static int erofs_raw_access_readpages(struct file *filp,
-                                     struct address_space *mapping,
-                                     struct list_head *pages,
-                                     unsigned int nr_pages)
+static void erofs_raw_access_readahead(struct readahead_control *rac)
 {
        erofs_off_t last_block;
        struct bio *bio = NULL;
-       gfp_t gfp = readahead_gfp_mask(mapping);
-       struct page *page = list_last_entry(pages, struct page, lru);
-
-       trace_erofs_readpages(mapping->host, page, nr_pages, true);
+       struct page *page;
 
-       for (; nr_pages; --nr_pages) {
-               page = list_entry(pages->prev, struct page, lru);
+       trace_erofs_readpages(rac->mapping->host, readahead_index(rac),
+                       readahead_count(rac), true);
 
+       while ((page = readahead_page(rac))) {
                prefetchw(&page->flags);
-               list_del(&page->lru);
 
-               if (!add_to_page_cache_lru(page, mapping, page->index, gfp)) {
-                       bio = erofs_read_raw_page(bio, mapping, page,
-                                                 &last_block, nr_pages, true);
+               bio = erofs_read_raw_page(bio, rac->mapping, page, &last_block,
+                               readahead_count(rac), true);
 
-                       /* all the page errors are ignored when readahead */
-                       if (IS_ERR(bio)) {
-                               pr_err("%s, readahead error at page %lu of nid %llu\n",
-                                      __func__, page->index,
-                                      EROFS_I(mapping->host)->nid);
+               /* all the page errors are ignored when readahead */
+               if (IS_ERR(bio)) {
+                       pr_err("%s, readahead error at page %lu of nid %llu\n",
+                              __func__, page->index,
+                              EROFS_I(rac->mapping->host)->nid);
 
-                               bio = NULL;
-                       }
+                       bio = NULL;
                }
 
-               /* pages could still be locked */
                put_page(page);
        }
-       DBG_BUGON(!list_empty(pages));
 
        /* the rare case (end in gaps) */
        if (bio)
                submit_bio(bio);
-       return 0;
 }
 
 static int erofs_get_block(struct inode *inode, sector_t iblock,
@@ -358,7 +347,7 @@ static sector_t erofs_bmap(struct address_space *mapping, sector_t block)
 /* for uncompressed (aligned) files and raw access for other files */
 const struct address_space_operations erofs_raw_access_aops = {
        .readpage = erofs_raw_access_readpage,
-       .readpages = erofs_raw_access_readpages,
+       .readahead = erofs_raw_access_readahead,
        .bmap = erofs_bmap,
 };
 
index 5d2d819406794a4e55708caa956095accdc88c67..7628816f245378f5fa3e196bbb56d09d92043a82 100644 (file)
@@ -274,7 +274,7 @@ static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq,
 
        i = 0;
        while (1) {
-               dst = vm_map_ram(rq->out, nrpages_out, -1, PAGE_KERNEL);
+               dst = vm_map_ram(rq->out, nrpages_out, -1);
 
                /* retry two more times (totally 3 times) */
                if (dst || ++i >= 3)
index c4b6c9aa87eccecaed8f375c100dbee9625f5772..187f93b4900e121c12d93f9c987d7ff15f842456 100644 (file)
@@ -1305,28 +1305,23 @@ static bool should_decompress_synchronously(struct erofs_sb_info *sbi,
        return nr <= sbi->max_sync_decompress_pages;
 }
 
-static int z_erofs_readpages(struct file *filp, struct address_space *mapping,
-                            struct list_head *pages, unsigned int nr_pages)
+static void z_erofs_readahead(struct readahead_control *rac)
 {
-       struct inode *const inode = mapping->host;
+       struct inode *const inode = rac->mapping->host;
        struct erofs_sb_info *const sbi = EROFS_I_SB(inode);
 
-       bool sync = should_decompress_synchronously(sbi, nr_pages);
+       bool sync = should_decompress_synchronously(sbi, readahead_count(rac));
        struct z_erofs_decompress_frontend f = DECOMPRESS_FRONTEND_INIT(inode);
-       gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
-       struct page *head = NULL;
+       struct page *page, *head = NULL;
        LIST_HEAD(pagepool);
 
-       trace_erofs_readpages(mapping->host, lru_to_page(pages),
-                             nr_pages, false);
+       trace_erofs_readpages(inode, readahead_index(rac),
+                       readahead_count(rac), false);
 
-       f.headoffset = (erofs_off_t)lru_to_page(pages)->index << PAGE_SHIFT;
-
-       for (; nr_pages; --nr_pages) {
-               struct page *page = lru_to_page(pages);
+       f.headoffset = readahead_pos(rac);
 
+       while ((page = readahead_page(rac))) {
                prefetchw(&page->flags);
-               list_del(&page->lru);
 
                /*
                 * A pure asynchronous readahead is indicated if
@@ -1335,11 +1330,6 @@ static int z_erofs_readpages(struct file *filp, struct address_space *mapping,
                 */
                sync &= !(PageReadahead(page) && !head);
 
-               if (add_to_page_cache_lru(page, mapping, page->index, gfp)) {
-                       list_add(&page->lru, &pagepool);
-                       continue;
-               }
-
                set_page_private(page, (unsigned long)head);
                head = page;
        }
@@ -1368,11 +1358,10 @@ static int z_erofs_readpages(struct file *filp, struct address_space *mapping,
 
        /* clean up the remaining free pages */
        put_pages_list(&pagepool);
-       return 0;
 }
 
 const struct address_space_operations z_erofs_aops = {
        .readpage = z_erofs_readpage,
-       .readpages = z_erofs_readpages,
+       .readahead = z_erofs_readahead,
 };
 
index 06887492f54b791506d2f7c76eb3ef073b72026d..785ead346543cb54d440ee1797bee941810f72b7 100644 (file)
@@ -372,10 +372,9 @@ static int exfat_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, exfat_get_block);
 }
 
-static int exfat_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned int nr_pages)
+static void exfat_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, exfat_get_block);
+       mpage_readahead(rac, exfat_get_block);
 }
 
 static int exfat_writepage(struct page *page, struct writeback_control *wbc)
@@ -502,7 +501,7 @@ int exfat_block_truncate_page(struct inode *inode, loff_t from)
 
 static const struct address_space_operations exfat_aops = {
        .readpage       = exfat_readpage,
-       .readpages      = exfat_readpages,
+       .readahead      = exfat_readahead,
        .writepage      = exfat_writepage,
        .writepages     = exfat_writepages,
        .write_begin    = exfat_write_begin,
index c885cf7d724b4830d0e952acbc91f0db1b3b1f63..2875c0a705b5987036f8ed252182cac1097e51a1 100644 (file)
@@ -877,11 +877,9 @@ static int ext2_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, ext2_get_block);
 }
 
-static int
-ext2_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned nr_pages)
+static void ext2_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);
+       mpage_readahead(rac, ext2_get_block);
 }
 
 static int
@@ -967,7 +965,7 @@ ext2_dax_writepages(struct address_space *mapping, struct writeback_control *wbc
 
 const struct address_space_operations ext2_aops = {
        .readpage               = ext2_readpage,
-       .readpages              = ext2_readpages,
+       .readahead              = ext2_readahead,
        .writepage              = ext2_writepage,
        .write_begin            = ext2_write_begin,
        .write_end              = ext2_write_end,
@@ -981,7 +979,7 @@ const struct address_space_operations ext2_aops = {
 
 const struct address_space_operations ext2_nobh_aops = {
        .readpage               = ext2_readpage,
-       .readpages              = ext2_readpages,
+       .readahead              = ext2_readahead,
        .writepage              = ext2_nobh_writepage,
        .write_begin            = ext2_nobh_write_begin,
        .write_end              = nobh_write_end,
index ad2dbf6e492451f79da28e88f73a383e241074e3..15b062efcff18429d43b3a402869f9fee749d055 100644 (file)
@@ -1357,11 +1357,9 @@ struct ext4_super_block {
  */
 #define EXT4_MF_MNTDIR_SAMPLED         0x0001
 #define EXT4_MF_FS_ABORTED             0x0002  /* Fatal error detected */
-#define EXT4_MF_TEST_DUMMY_ENCRYPTION  0x0004
 
 #ifdef CONFIG_FS_ENCRYPTION
-#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \
-                                               EXT4_MF_TEST_DUMMY_ENCRYPTION))
+#define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_ctx.ctx != NULL)
 #else
 #define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
 #endif
@@ -1551,6 +1549,9 @@ struct ext4_sb_info {
        struct ratelimit_state s_warning_ratelimit_state;
        struct ratelimit_state s_msg_ratelimit_state;
 
+       /* Encryption context for '-o test_dummy_encryption' */
+       struct fscrypt_dummy_context s_dummy_enc_ctx;
+
        /*
         * Barrier between writepages ops and changing any inode's JOURNAL_DATA
         * or EXTENTS flag.
@@ -3316,9 +3317,8 @@ static inline void ext4_set_de_type(struct super_block *sb,
 }
 
 /* readpages.c */
-extern int ext4_mpage_readpages(struct address_space *mapping,
-                               struct list_head *pages, struct page *page,
-                               unsigned nr_pages, bool is_readahead);
+extern int ext4_mpage_readpages(struct inode *inode,
+               struct readahead_control *rac, struct page *page);
 extern int __init ext4_init_post_read_processing(void);
 extern void ext4_exit_post_read_processing(void);
 
index 2a4aae6acdcb9e4acb79647baa24c218fb6cca2a..52be85f9615941f503386f5932b9794206722575 100644 (file)
@@ -3224,23 +3224,20 @@ static int ext4_readpage(struct file *file, struct page *page)
                ret = ext4_readpage_inline(inode, page);
 
        if (ret == -EAGAIN)
-               return ext4_mpage_readpages(page->mapping, NULL, page, 1,
-                                               false);
+               return ext4_mpage_readpages(inode, NULL, page);
 
        return ret;
 }
 
-static int
-ext4_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned nr_pages)
+static void ext4_readahead(struct readahead_control *rac)
 {
-       struct inode *inode = mapping->host;
+       struct inode *inode = rac->mapping->host;
 
-       /* If the file has inline data, no need to do readpages. */
+       /* If the file has inline data, no need to do readahead. */
        if (ext4_has_inline_data(inode))
-               return 0;
+               return;
 
-       return ext4_mpage_readpages(mapping, pages, NULL, nr_pages, true);
+       ext4_mpage_readpages(inode, rac, NULL);
 }
 
 static void ext4_invalidatepage(struct page *page, unsigned int offset,
@@ -3605,7 +3602,7 @@ static int ext4_set_page_dirty(struct page *page)
 
 static const struct address_space_operations ext4_aops = {
        .readpage               = ext4_readpage,
-       .readpages              = ext4_readpages,
+       .readahead              = ext4_readahead,
        .writepage              = ext4_writepage,
        .writepages             = ext4_writepages,
        .write_begin            = ext4_write_begin,
@@ -3622,7 +3619,7 @@ static const struct address_space_operations ext4_aops = {
 
 static const struct address_space_operations ext4_journalled_aops = {
        .readpage               = ext4_readpage,
-       .readpages              = ext4_readpages,
+       .readahead              = ext4_readahead,
        .writepage              = ext4_writepage,
        .writepages             = ext4_writepages,
        .write_begin            = ext4_write_begin,
@@ -3638,7 +3635,7 @@ static const struct address_space_operations ext4_journalled_aops = {
 
 static const struct address_space_operations ext4_da_aops = {
        .readpage               = ext4_readpage,
-       .readpages              = ext4_readpages,
+       .readahead              = ext4_readahead,
        .writepage              = ext4_writepage,
        .writepages             = ext4_writepages,
        .write_begin            = ext4_da_write_begin,
index c1769afbf799530b8ce61e5815cb22fa51570e5d..5761e9961682ef156f2d29553d84aee1634b643f 100644 (file)
@@ -7,8 +7,8 @@
  *
  * This was originally taken from fs/mpage.c
  *
- * The intent is the ext4_mpage_readpages() function here is intended
- * to replace mpage_readpages() in the general case, not just for
+ * The ext4_mpage_readpages() function here is intended to
+ * replace mpage_readahead() in the general case, not just for
  * encrypted files.  It has some limitations (see below), where it
  * will fall back to read_block_full_page(), but these limitations
  * should only be hit when page_size != block_size.
@@ -221,14 +221,12 @@ static inline loff_t ext4_readpage_limit(struct inode *inode)
        return i_size_read(inode);
 }
 
-int ext4_mpage_readpages(struct address_space *mapping,
-                        struct list_head *pages, struct page *page,
-                        unsigned nr_pages, bool is_readahead)
+int ext4_mpage_readpages(struct inode *inode,
+               struct readahead_control *rac, struct page *page)
 {
        struct bio *bio = NULL;
        sector_t last_block_in_bio = 0;
 
-       struct inode *inode = mapping->host;
        const unsigned blkbits = inode->i_blkbits;
        const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
        const unsigned blocksize = 1 << blkbits;
@@ -241,6 +239,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
        int length;
        unsigned relative_block = 0;
        struct ext4_map_blocks map;
+       unsigned int nr_pages = rac ? readahead_count(rac) : 1;
 
        map.m_pblk = 0;
        map.m_lblk = 0;
@@ -251,14 +250,9 @@ int ext4_mpage_readpages(struct address_space *mapping,
                int fully_mapped = 1;
                unsigned first_hole = blocks_per_page;
 
-               if (pages) {
-                       page = lru_to_page(pages);
-
+               if (rac) {
+                       page = readahead_page(rac);
                        prefetchw(&page->flags);
-                       list_del(&page->lru);
-                       if (add_to_page_cache_lru(page, mapping, page->index,
-                                 readahead_gfp_mask(mapping)))
-                               goto next_page;
                }
 
                if (page_has_buffers(page))
@@ -381,7 +375,7 @@ int ext4_mpage_readpages(struct address_space *mapping,
                        bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
                        bio->bi_end_io = mpage_end_io;
                        bio_set_op_attrs(bio, REQ_OP_READ,
-                                               is_readahead ? REQ_RAHEAD : 0);
+                                               rac ? REQ_RAHEAD : 0);
                }
 
                length = first_hole << blkbits;
@@ -406,10 +400,9 @@ int ext4_mpage_readpages(struct address_space *mapping,
                else
                        unlock_page(page);
        next_page:
-               if (pages)
+               if (rac)
                        put_page(page);
        }
-       BUG_ON(pages && !list_empty(pages));
        if (bio)
                submit_bio(bio);
        return 0;
index bf5fcb477f667211f031c6f1cff42f54a1a5235d..4a3d21972011bb907a35483ba669b283622faf46 100644 (file)
@@ -1106,6 +1106,7 @@ static void ext4_put_super(struct super_block *sb)
                crypto_free_shash(sbi->s_chksum_driver);
        kfree(sbi->s_blockgroup_lock);
        fs_put_dax(sbi->s_daxdev);
+       fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
 #ifdef CONFIG_UNICODE
        utf8_unload(sbi->s_encoding);
 #endif
@@ -1389,9 +1390,10 @@ retry:
        return res;
 }
 
-static bool ext4_dummy_context(struct inode *inode)
+static const union fscrypt_context *
+ext4_get_dummy_context(struct super_block *sb)
 {
-       return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb));
+       return EXT4_SB(sb)->s_dummy_enc_ctx.ctx;
 }
 
 static bool ext4_has_stable_inodes(struct super_block *sb)
@@ -1410,7 +1412,7 @@ static const struct fscrypt_operations ext4_cryptops = {
        .key_prefix             = "ext4:",
        .get_context            = ext4_get_context,
        .set_context            = ext4_set_context,
-       .dummy_context          = ext4_dummy_context,
+       .get_dummy_context      = ext4_get_dummy_context,
        .empty_dir              = ext4_empty_dir,
        .max_namelen            = EXT4_NAME_LEN,
        .has_stable_inodes      = ext4_has_stable_inodes,
@@ -1605,6 +1607,7 @@ static const match_table_t tokens = {
        {Opt_init_itable, "init_itable"},
        {Opt_noinit_itable, "noinit_itable"},
        {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
+       {Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
        {Opt_test_dummy_encryption, "test_dummy_encryption"},
        {Opt_nombcache, "nombcache"},
        {Opt_nombcache, "no_mbcache"},  /* for backward compatibility */
@@ -1816,7 +1819,7 @@ static const struct mount_opts {
        {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
        {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
        {Opt_max_dir_size_kb, 0, MOPT_GTE0},
-       {Opt_test_dummy_encryption, 0, MOPT_GTE0},
+       {Opt_test_dummy_encryption, 0, MOPT_STRING},
        {Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
        {Opt_err, 0, 0}
 };
@@ -1851,6 +1854,48 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
 }
 #endif
 
+static int ext4_set_test_dummy_encryption(struct super_block *sb,
+                                         const char *opt,
+                                         const substring_t *arg,
+                                         bool is_remount)
+{
+#ifdef CONFIG_FS_ENCRYPTION
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       int err;
+
+       /*
+        * This mount option is just for testing, and it's not worthwhile to
+        * implement the extra complexity (e.g. RCU protection) that would be
+        * needed to allow it to be set or changed during remount.  We do allow
+        * it to be specified during remount, but only if there is no change.
+        */
+       if (is_remount && !sbi->s_dummy_enc_ctx.ctx) {
+               ext4_msg(sb, KERN_WARNING,
+                        "Can't set test_dummy_encryption on remount");
+               return -1;
+       }
+       err = fscrypt_set_test_dummy_encryption(sb, arg, &sbi->s_dummy_enc_ctx);
+       if (err) {
+               if (err == -EEXIST)
+                       ext4_msg(sb, KERN_WARNING,
+                                "Can't change test_dummy_encryption on remount");
+               else if (err == -EINVAL)
+                       ext4_msg(sb, KERN_WARNING,
+                                "Value of option \"%s\" is unrecognized", opt);
+               else
+                       ext4_msg(sb, KERN_WARNING,
+                                "Error processing option \"%s\" [%d]",
+                                opt, err);
+               return -1;
+       }
+       ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
+#else
+       ext4_msg(sb, KERN_WARNING,
+                "Test dummy encryption mount option ignored");
+#endif
+       return 1;
+}
+
 static int handle_mount_opt(struct super_block *sb, char *opt, int token,
                            substring_t *args, unsigned long *journal_devnum,
                            unsigned int *journal_ioprio, int is_remount)
@@ -2047,14 +2092,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
                *journal_ioprio =
                        IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
        } else if (token == Opt_test_dummy_encryption) {
-#ifdef CONFIG_FS_ENCRYPTION
-               sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
-               ext4_msg(sb, KERN_WARNING,
-                        "Test dummy encryption mode enabled");
-#else
-               ext4_msg(sb, KERN_WARNING,
-                        "Test dummy encryption mount option ignored");
-#endif
+               return ext4_set_test_dummy_encryption(sb, opt, &args[0],
+                                                     is_remount);
        } else if (m->flags & MOPT_DATAJ) {
                if (is_remount) {
                        if (!sbi->s_journal)
@@ -2311,8 +2350,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
                SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
        if (test_opt(sb, DATA_ERR_ABORT))
                SEQ_OPTS_PUTS("data_err=abort");
-       if (DUMMY_ENCRYPTION_ENABLED(sbi))
-               SEQ_OPTS_PUTS("test_dummy_encryption");
+
+       fscrypt_show_test_dummy_encryption(seq, sep, sb);
 
        ext4_show_quota_options(seq, sb);
        return 0;
@@ -4780,6 +4819,7 @@ failed_mount:
        for (i = 0; i < EXT4_MAXQUOTAS; i++)
                kfree(get_qf_name(sb, sbi, i));
 #endif
+       fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
        ext4_blkdev_remove(sbi);
        brelse(bh);
 out_fail:
index 04bfaf63752c388f16215f3922b78c2aa1fa4e6a..6c9fc9e21c138aded7e3d1fad09349aca1f998e8 100644 (file)
@@ -293,6 +293,7 @@ EXT4_ATTR_FEATURE(batched_discard);
 EXT4_ATTR_FEATURE(meta_bg_resize);
 #ifdef CONFIG_FS_ENCRYPTION
 EXT4_ATTR_FEATURE(encryption);
+EXT4_ATTR_FEATURE(test_dummy_encryption_v2);
 #endif
 #ifdef CONFIG_UNICODE
 EXT4_ATTR_FEATURE(casefold);
@@ -308,6 +309,7 @@ static struct attribute *ext4_feat_attrs[] = {
        ATTR_LIST(meta_bg_resize),
 #ifdef CONFIG_FS_ENCRYPTION
        ATTR_LIST(encryption),
+       ATTR_LIST(test_dummy_encryption_v2),
 #endif
 #ifdef CONFIG_UNICODE
        ATTR_LIST(casefold),
index dc5ec724d889108b2c62940942e1c14013a33c98..dec1244dd062b02aa74f3ef1c5225d6e4e4a101c 100644 (file)
@@ -342,37 +342,6 @@ static int ext4_get_verity_descriptor(struct inode *inode, void *buf,
        return desc_size;
 }
 
-/*
- * Prefetch some pages from the file's Merkle tree.
- *
- * This is basically a stripped-down version of __do_page_cache_readahead()
- * which works on pages past i_size.
- */
-static void ext4_merkle_tree_readahead(struct address_space *mapping,
-                                      pgoff_t start_index, unsigned long count)
-{
-       LIST_HEAD(pages);
-       unsigned int nr_pages = 0;
-       struct page *page;
-       pgoff_t index;
-       struct blk_plug plug;
-
-       for (index = start_index; index < start_index + count; index++) {
-               page = xa_load(&mapping->i_pages, index);
-               if (!page || xa_is_value(page)) {
-                       page = __page_cache_alloc(readahead_gfp_mask(mapping));
-                       if (!page)
-                               break;
-                       page->index = index;
-                       list_add(&page->lru, &pages);
-                       nr_pages++;
-               }
-       }
-       blk_start_plug(&plug);
-       ext4_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
-       blk_finish_plug(&plug);
-}
-
 static struct page *ext4_read_merkle_tree_page(struct inode *inode,
                                               pgoff_t index,
                                               unsigned long num_ra_pages)
@@ -386,8 +355,8 @@ static struct page *ext4_read_merkle_tree_page(struct inode *inode,
                if (page)
                        put_page(page);
                else if (num_ra_pages > 1)
-                       ext4_merkle_tree_readahead(inode->i_mapping, index,
-                                                  num_ra_pages);
+                       page_cache_readahead_unbounded(inode->i_mapping, NULL,
+                                       index, num_ra_pages, 0);
                page = read_mapping_page(inode->i_mapping, index, NULL);
        }
        return page;
index cdf2f626bea7abc896a5379a1fa815b5367f0afc..03ec97f28235a52874aa78afc553a917e2c7ab71 100644 (file)
@@ -2177,13 +2177,11 @@ out:
  * use ->readpage() or do the necessary surgery to decouple ->readpages()
  * from read-ahead.
  */
-int f2fs_mpage_readpages(struct address_space *mapping,
-                       struct list_head *pages, struct page *page,
-                       unsigned nr_pages, bool is_readahead)
+static int f2fs_mpage_readpages(struct inode *inode,
+               struct readahead_control *rac, struct page *page)
 {
        struct bio *bio = NULL;
        sector_t last_block_in_bio = 0;
-       struct inode *inode = mapping->host;
        struct f2fs_map_blocks map;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
        struct compress_ctx cc = {
@@ -2197,6 +2195,7 @@ int f2fs_mpage_readpages(struct address_space *mapping,
                .nr_cpages = 0,
        };
 #endif
+       unsigned nr_pages = rac ? readahead_count(rac) : 1;
        unsigned max_nr_pages = nr_pages;
        int ret = 0;
 
@@ -2210,15 +2209,9 @@ int f2fs_mpage_readpages(struct address_space *mapping,
        map.m_may_create = false;
 
        for (; nr_pages; nr_pages--) {
-               if (pages) {
-                       page = list_last_entry(pages, struct page, lru);
-
+               if (rac) {
+                       page = readahead_page(rac);
                        prefetchw(&page->flags);
-                       list_del(&page->lru);
-                       if (add_to_page_cache_lru(page, mapping,
-                                                 page_index(page),
-                                                 readahead_gfp_mask(mapping)))
-                               goto next_page;
                }
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
@@ -2228,7 +2221,7 @@ int f2fs_mpage_readpages(struct address_space *mapping,
                                ret = f2fs_read_multi_pages(&cc, &bio,
                                                        max_nr_pages,
                                                        &last_block_in_bio,
-                                                       is_readahead, false);
+                                                       rac != NULL, false);
                                f2fs_destroy_compress_ctx(&cc);
                                if (ret)
                                        goto set_error_page;
@@ -2251,7 +2244,7 @@ read_single_page:
 #endif
 
                ret = f2fs_read_single_page(inode, page, max_nr_pages, &map,
-                                       &bio, &last_block_in_bio, is_readahead);
+                                       &bio, &last_block_in_bio, rac);
                if (ret) {
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 set_error_page:
@@ -2260,8 +2253,10 @@ set_error_page:
                        zero_user_segment(page, 0, PAGE_SIZE);
                        unlock_page(page);
                }
+#ifdef CONFIG_F2FS_FS_COMPRESSION
 next_page:
-               if (pages)
+#endif
+               if (rac)
                        put_page(page);
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
@@ -2271,16 +2266,15 @@ next_page:
                                ret = f2fs_read_multi_pages(&cc, &bio,
                                                        max_nr_pages,
                                                        &last_block_in_bio,
-                                                       is_readahead, false);
+                                                       rac != NULL, false);
                                f2fs_destroy_compress_ctx(&cc);
                        }
                }
 #endif
        }
-       BUG_ON(pages && !list_empty(pages));
        if (bio)
                __submit_bio(F2FS_I_SB(inode), bio, DATA);
-       return pages ? 0 : ret;
+       return ret;
 }
 
 static int f2fs_read_data_page(struct file *file, struct page *page)
@@ -2299,28 +2293,24 @@ static int f2fs_read_data_page(struct file *file, struct page *page)
        if (f2fs_has_inline_data(inode))
                ret = f2fs_read_inline_data(inode, page);
        if (ret == -EAGAIN)
-               ret = f2fs_mpage_readpages(page_file_mapping(page),
-                                               NULL, page, 1, false);
+               ret = f2fs_mpage_readpages(inode, NULL, page);
        return ret;
 }
 
-static int f2fs_read_data_pages(struct file *file,
-                       struct address_space *mapping,
-                       struct list_head *pages, unsigned nr_pages)
+static void f2fs_readahead(struct readahead_control *rac)
 {
-       struct inode *inode = mapping->host;
-       struct page *page = list_last_entry(pages, struct page, lru);
+       struct inode *inode = rac->mapping->host;
 
-       trace_f2fs_readpages(inode, page, nr_pages);
+       trace_f2fs_readpages(inode, readahead_index(rac), readahead_count(rac));
 
        if (!f2fs_is_compress_backend_ready(inode))
-               return 0;
+               return;
 
        /* If the file has inline data, skip readpages */
        if (f2fs_has_inline_data(inode))
-               return 0;
+               return;
 
-       return f2fs_mpage_readpages(mapping, pages, NULL, nr_pages, true);
+       f2fs_mpage_readpages(inode, rac, NULL);
 }
 
 int f2fs_encrypt_one_page(struct f2fs_io_info *fio)
@@ -3805,7 +3795,7 @@ static void f2fs_swap_deactivate(struct file *file)
 
 const struct address_space_operations f2fs_dblock_aops = {
        .readpage       = f2fs_read_data_page,
-       .readpages      = f2fs_read_data_pages,
+       .readahead      = f2fs_readahead,
        .writepage      = f2fs_write_data_page,
        .writepages     = f2fs_write_data_pages,
        .write_begin    = f2fs_write_begin,
index ba470d5687fe04417a83e618590a5ad6c0c4582a..5c0149d2f46a4fec579eb2373a59432eddbfb758 100644 (file)
@@ -138,7 +138,7 @@ struct f2fs_mount_info {
        int fsync_mode;                 /* fsync policy */
        int fs_mode;                    /* fs mode: LFS or ADAPTIVE */
        int bggc_mode;                  /* bggc mode: off, on or sync */
-       bool test_dummy_encryption;     /* test dummy encryption */
+       struct fscrypt_dummy_context dummy_enc_ctx; /* test dummy encryption */
        block_t unusable_cap;           /* Amount of space allowed to be
                                         * unusable when disabling checkpoint
                                         */
@@ -1259,7 +1259,7 @@ enum fsync_mode {
 
 #ifdef CONFIG_FS_ENCRYPTION
 #define DUMMY_ENCRYPTION_ENABLED(sbi) \
-                       (unlikely(F2FS_OPTION(sbi).test_dummy_encryption))
+       (unlikely(F2FS_OPTION(sbi).dummy_enc_ctx.ctx != NULL))
 #else
 #define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
 #endif
@@ -3051,19 +3051,12 @@ static inline void f2fs_set_page_private(struct page *page,
        if (PagePrivate(page))
                return;
 
-       get_page(page);
-       SetPagePrivate(page);
-       set_page_private(page, data);
+       attach_page_private(page, (void *)data);
 }
 
 static inline void f2fs_clear_page_private(struct page *page)
 {
-       if (!PagePrivate(page))
-               return;
-
-       set_page_private(page, 0);
-       ClearPagePrivate(page);
-       f2fs_put_page(page, 0);
+       detach_page_private(page);
 }
 
 /*
@@ -3373,9 +3366,6 @@ int f2fs_reserve_new_block(struct dnode_of_data *dn);
 int f2fs_get_block(struct dnode_of_data *dn, pgoff_t index);
 int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *from);
 int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index);
-int f2fs_mpage_readpages(struct address_space *mapping,
-                       struct list_head *pages, struct page *page,
-                       unsigned nr_pages, bool is_readahead);
 struct page *f2fs_get_read_data_page(struct inode *inode, pgoff_t index,
                        int op_flags, bool for_write);
 struct page *f2fs_find_data_page(struct inode *inode, pgoff_t index);
index 5bc4dcd8fc03fbbb97ece6233c925634dc972fa8..8c4ea5003ef8cb7b50081590ea2ed07c91f5236b 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/types.h>
 #include <linux/fs.h>
 #include <linux/f2fs_fs.h>
-#include <linux/cryptohash.h>
 #include <linux/pagemap.h>
 #include <linux/unicode.h>
 
index f2dfc21c6abb0af8c09775a7b8ebdeca0ec4ba5b..8a9955902d849ce3fbab8c12ff9611a33d18cbee 100644 (file)
@@ -202,6 +202,7 @@ static match_table_t f2fs_tokens = {
        {Opt_whint, "whint_mode=%s"},
        {Opt_alloc, "alloc_mode=%s"},
        {Opt_fsync, "fsync_mode=%s"},
+       {Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
        {Opt_test_dummy_encryption, "test_dummy_encryption"},
        {Opt_checkpoint_disable, "checkpoint=disable"},
        {Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
@@ -394,7 +395,52 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 }
 #endif
 
-static int parse_options(struct super_block *sb, char *options)
+static int f2fs_set_test_dummy_encryption(struct super_block *sb,
+                                         const char *opt,
+                                         const substring_t *arg,
+                                         bool is_remount)
+{
+       struct f2fs_sb_info *sbi = F2FS_SB(sb);
+#ifdef CONFIG_FS_ENCRYPTION
+       int err;
+
+       if (!f2fs_sb_has_encrypt(sbi)) {
+               f2fs_err(sbi, "Encrypt feature is off");
+               return -EINVAL;
+       }
+
+       /*
+        * This mount option is just for testing, and it's not worthwhile to
+        * implement the extra complexity (e.g. RCU protection) that would be
+        * needed to allow it to be set or changed during remount.  We do allow
+        * it to be specified during remount, but only if there is no change.
+        */
+       if (is_remount && !F2FS_OPTION(sbi).dummy_enc_ctx.ctx) {
+               f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
+               return -EINVAL;
+       }
+       err = fscrypt_set_test_dummy_encryption(
+               sb, arg, &F2FS_OPTION(sbi).dummy_enc_ctx);
+       if (err) {
+               if (err == -EEXIST)
+                       f2fs_warn(sbi,
+                                 "Can't change test_dummy_encryption on remount");
+               else if (err == -EINVAL)
+                       f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
+                                 opt);
+               else
+                       f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
+                                 opt, err);
+               return -EINVAL;
+       }
+       f2fs_warn(sbi, "Test dummy encryption mode enabled");
+#else
+       f2fs_warn(sbi, "Test dummy encryption mount option ignored");
+#endif
+       return 0;
+}
+
+static int parse_options(struct super_block *sb, char *options, bool is_remount)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
        substring_t args[MAX_OPT_ARGS];
@@ -403,9 +449,7 @@ static int parse_options(struct super_block *sb, char *options)
        int arg = 0, ext_cnt;
        kuid_t uid;
        kgid_t gid;
-#ifdef CONFIG_QUOTA
        int ret;
-#endif
 
        if (!options)
                return 0;
@@ -778,17 +822,10 @@ static int parse_options(struct super_block *sb, char *options)
                        kvfree(name);
                        break;
                case Opt_test_dummy_encryption:
-#ifdef CONFIG_FS_ENCRYPTION
-                       if (!f2fs_sb_has_encrypt(sbi)) {
-                               f2fs_err(sbi, "Encrypt feature is off");
-                               return -EINVAL;
-                       }
-
-                       F2FS_OPTION(sbi).test_dummy_encryption = true;
-                       f2fs_info(sbi, "Test dummy encryption mode enabled");
-#else
-                       f2fs_info(sbi, "Test dummy encryption mount option ignored");
-#endif
+                       ret = f2fs_set_test_dummy_encryption(sb, p, &args[0],
+                                                            is_remount);
+                       if (ret)
+                               return ret;
                        break;
                case Opt_checkpoint_disable_cap_perc:
                        if (args->from && match_int(args, &arg))
@@ -1213,6 +1250,7 @@ static void f2fs_put_super(struct super_block *sb)
        for (i = 0; i < MAXQUOTAS; i++)
                kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
 #endif
+       fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
        destroy_percpu_info(sbi);
        for (i = 0; i < NR_PAGE_TYPE; i++)
                kvfree(sbi->write_io[i]);
@@ -1543,10 +1581,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_printf(seq, ",whint_mode=%s", "user-based");
        else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS)
                seq_printf(seq, ",whint_mode=%s", "fs-based");
-#ifdef CONFIG_FS_ENCRYPTION
-       if (F2FS_OPTION(sbi).test_dummy_encryption)
-               seq_puts(seq, ",test_dummy_encryption");
-#endif
+
+       fscrypt_show_test_dummy_encryption(seq, ',', sbi->sb);
 
        if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_DEFAULT)
                seq_printf(seq, ",alloc_mode=%s", "default");
@@ -1575,7 +1611,6 @@ static void default_options(struct f2fs_sb_info *sbi)
        F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
        F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
        F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
-       F2FS_OPTION(sbi).test_dummy_encryption = false;
        F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
        F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
        F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZ4;
@@ -1734,7 +1769,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
        default_options(sbi);
 
        /* parse mount options */
-       err = parse_options(sb, data);
+       err = parse_options(sb, data, true);
        if (err)
                goto restore_opts;
        checkpoint_changed =
@@ -2410,9 +2445,10 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
                                ctx, len, fs_data, XATTR_CREATE);
 }
 
-static bool f2fs_dummy_context(struct inode *inode)
+static const union fscrypt_context *
+f2fs_get_dummy_context(struct super_block *sb)
 {
-       return DUMMY_ENCRYPTION_ENABLED(F2FS_I_SB(inode));
+       return F2FS_OPTION(F2FS_SB(sb)).dummy_enc_ctx.ctx;
 }
 
 static bool f2fs_has_stable_inodes(struct super_block *sb)
@@ -2431,7 +2467,7 @@ static const struct fscrypt_operations f2fs_cryptops = {
        .key_prefix             = "f2fs:",
        .get_context            = f2fs_get_context,
        .set_context            = f2fs_set_context,
-       .dummy_context          = f2fs_dummy_context,
+       .get_dummy_context      = f2fs_get_dummy_context,
        .empty_dir              = f2fs_empty_dir,
        .max_namelen            = F2FS_NAME_LEN,
        .has_stable_inodes      = f2fs_has_stable_inodes,
@@ -3366,7 +3402,7 @@ try_onemore:
                goto free_sb_buf;
        }
 
-       err = parse_options(sb, options);
+       err = parse_options(sb, options, false);
        if (err)
                goto free_options;
 
@@ -3769,6 +3805,7 @@ free_options:
        for (i = 0; i < MAXQUOTAS; i++)
                kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
 #endif
+       fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
        kvfree(options);
 free_sb_buf:
        kvfree(raw_super);
index e3bbbef9b4f09e4ec5d8d91028ce81c1aa3bbb73..3162f46b3c9bfc1c1b7eefaf3cb5f650e949bbe9 100644 (file)
@@ -446,6 +446,7 @@ enum feat_id {
        FEAT_SB_CHECKSUM,
        FEAT_CASEFOLD,
        FEAT_COMPRESSION,
+       FEAT_TEST_DUMMY_ENCRYPTION_V2,
 };
 
 static ssize_t f2fs_feature_show(struct f2fs_attr *a,
@@ -466,6 +467,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
        case FEAT_SB_CHECKSUM:
        case FEAT_CASEFOLD:
        case FEAT_COMPRESSION:
+       case FEAT_TEST_DUMMY_ENCRYPTION_V2:
                return sprintf(buf, "supported\n");
        }
        return 0;
@@ -563,6 +565,7 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks);
 
 #ifdef CONFIG_FS_ENCRYPTION
 F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
+F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2, FEAT_TEST_DUMMY_ENCRYPTION_V2);
 #endif
 #ifdef CONFIG_BLK_DEV_ZONED
 F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED);
@@ -647,6 +650,7 @@ ATTRIBUTE_GROUPS(f2fs);
 static struct attribute *f2fs_feat_attrs[] = {
 #ifdef CONFIG_FS_ENCRYPTION
        ATTR_LIST(encryption),
+       ATTR_LIST(test_dummy_encryption_v2),
 #endif
 #ifdef CONFIG_BLK_DEV_ZONED
        ATTR_LIST(block_zoned),
index d7d430a6f130555997847557794562a80c39b9e0..865c9fb774fbeb5209824bd68fb46d1544dfb703 100644 (file)
@@ -222,37 +222,6 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
        return size;
 }
 
-/*
- * Prefetch some pages from the file's Merkle tree.
- *
- * This is basically a stripped-down version of __do_page_cache_readahead()
- * which works on pages past i_size.
- */
-static void f2fs_merkle_tree_readahead(struct address_space *mapping,
-                                      pgoff_t start_index, unsigned long count)
-{
-       LIST_HEAD(pages);
-       unsigned int nr_pages = 0;
-       struct page *page;
-       pgoff_t index;
-       struct blk_plug plug;
-
-       for (index = start_index; index < start_index + count; index++) {
-               page = xa_load(&mapping->i_pages, index);
-               if (!page || xa_is_value(page)) {
-                       page = __page_cache_alloc(readahead_gfp_mask(mapping));
-                       if (!page)
-                               break;
-                       page->index = index;
-                       list_add(&page->lru, &pages);
-                       nr_pages++;
-               }
-       }
-       blk_start_plug(&plug);
-       f2fs_mpage_readpages(mapping, &pages, NULL, nr_pages, true);
-       blk_finish_plug(&plug);
-}
-
 static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
                                               pgoff_t index,
                                               unsigned long num_ra_pages)
@@ -266,8 +235,8 @@ static struct page *f2fs_read_merkle_tree_page(struct inode *inode,
                if (page)
                        put_page(page);
                else if (num_ra_pages > 1)
-                       f2fs_merkle_tree_readahead(inode->i_mapping, index,
-                                                  num_ra_pages);
+                       page_cache_readahead_unbounded(inode->i_mapping, NULL,
+                                       index, num_ra_pages, 0);
                page = read_mapping_page(inode->i_mapping, index, NULL);
        }
        return page;
index 718163d0c621811c6f6d6159c2512a0a6d8340d5..ca31993dcb47c9159c10d428d352420591c08d30 100644 (file)
@@ -69,7 +69,7 @@ config VFAT_FS
 
          The VFAT support enlarges your kernel by about 10 KB and it only
          works if you said Y to the "DOS FAT fs support" above.  Please read
-         the file <file:Documentation/filesystems/vfat.txt> for details.  If
+         the file <file:Documentation/filesystems/vfat.rst> for details.  If
          unsure, say Y.
 
          To compile this as a module, choose M here: the module will be called
@@ -82,7 +82,7 @@ config FAT_DEFAULT_CODEPAGE
        help
          This option should be set to the codepage of your FAT filesystems.
          It can be overridden with the "codepage" mount option.
-         See <file:Documentation/filesystems/vfat.txt> for more information.
+         See <file:Documentation/filesystems/vfat.rst> for more information.
 
 config FAT_DEFAULT_IOCHARSET
        string "Default iocharset for FAT"
@@ -96,7 +96,7 @@ config FAT_DEFAULT_IOCHARSET
          Note that "utf8" is not recommended for FAT filesystems.
          If unsure, you shouldn't set "utf8" here - select the next option
          instead if you would like to use UTF-8 encoded file names by default.
-         See <file:Documentation/filesystems/vfat.txt> for more information.
+         See <file:Documentation/filesystems/vfat.rst> for more information.
 
          Enable any character sets you need in File Systems/Native Language
          Support.
@@ -114,4 +114,4 @@ config FAT_DEFAULT_UTF8
 
          Say Y if you use UTF-8 encoding for file names, N otherwise.
 
-         See <file:Documentation/filesystems/vfat.txt> for more information.
+         See <file:Documentation/filesystems/vfat.rst> for more information.
index 054acd9fd0331dc0ba6ad74473c4d5117569ff60..b4ddf48fa4449db8f67fa38e7f037d3c6b93d0ca 100644 (file)
@@ -804,8 +804,6 @@ static long fat_dir_ioctl(struct file *filp, unsigned int cmd,
                return fat_generic_ioctl(filp, cmd, arg);
        }
 
-       if (!access_ok(d1, sizeof(struct __fat_dirent[2])))
-               return -EFAULT;
        /*
         * Yes, we don't need this put_user() absolutely. However old
         * code didn't return the right value. So, app use this value,
@@ -844,8 +842,6 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
                return fat_generic_ioctl(filp, cmd, (unsigned long)arg);
        }
 
-       if (!access_ok(d1, sizeof(struct compat_dirent[2])))
-               return -EFAULT;
        /*
         * Yes, we don't need this put_user() absolutely. However old
         * code didn't return the right value. So, app use this value,
index 71946da8438849859b5240d199b216cf0b424f55..e6e68b2274a5cc5fbd3d4954cab5ee9f7f5d97fd 100644 (file)
@@ -210,10 +210,9 @@ static int fat_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, fat_get_block);
 }
 
-static int fat_readpages(struct file *file, struct address_space *mapping,
-                        struct list_head *pages, unsigned nr_pages)
+static void fat_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
+       mpage_readahead(rac, fat_get_block);
 }
 
 static void fat_write_failed(struct address_space *mapping, loff_t to)
@@ -344,7 +343,7 @@ int fat_block_truncate_page(struct inode *inode, loff_t from)
 
 static const struct address_space_operations fat_aops = {
        .readpage       = fat_readpage,
-       .readpages      = fat_readpages,
+       .readahead      = fat_readahead,
        .writepage      = fat_writepage,
        .writepages     = fat_writepages,
        .write_begin    = fat_write_begin,
index 30d55c9a1744a6244d50e8ae3497a1563e7e14a1..676e620948d24291b894b93f9edce8280bc21cd1 100644 (file)
@@ -198,6 +198,7 @@ static struct file *alloc_file(const struct path *path, int flags,
        file->f_inode = path->dentry->d_inode;
        file->f_mapping = path->dentry->d_inode->i_mapping;
        file->f_wb_err = filemap_sample_wb_err(file->f_mapping);
+       file->f_sb_err = file_sample_sb_err(file);
        if ((file->f_mode & FMODE_READ) &&
             likely(fop->read || fop->read_iter))
                file->f_mode |= FMODE_CAN_READ;
index 76ac9c7d32ec7ee2e719e48d2956276676810d92..c5bdf46e3b4bc743002e5261a6182d78193e7c6f 100644 (file)
@@ -1070,7 +1070,6 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
 static unsigned long get_nr_dirty_pages(void)
 {
        return global_node_page_state(NR_FILE_DIRTY) +
-               global_node_page_state(NR_UNSTABLE_NFS) +
                get_nr_dirty_inodes();
 }
 
index fc9f6ef93b55d155017a203c7d8a93dfed952155..7d5c5dd2b1d5af34559dfcca079089aa93ddec37 100644 (file)
@@ -42,7 +42,6 @@ static const struct constant_table common_set_sb_flag[] = {
        { "dirsync",    SB_DIRSYNC },
        { "lazytime",   SB_LAZYTIME },
        { "mand",       SB_MANDLOCK },
-       { "posixacl",   SB_POSIXACL },
        { "ro",         SB_RDONLY },
        { "sync",       SB_SYNCHRONOUS },
        { },
@@ -53,44 +52,15 @@ static const struct constant_table common_clear_sb_flag[] = {
        { "nolazytime", SB_LAZYTIME },
        { "nomand",     SB_MANDLOCK },
        { "rw",         SB_RDONLY },
-       { "silent",     SB_SILENT },
        { },
 };
 
-static const char *const forbidden_sb_flag[] = {
-       "bind",
-       "dev",
-       "exec",
-       "move",
-       "noatime",
-       "nodev",
-       "nodiratime",
-       "noexec",
-       "norelatime",
-       "nostrictatime",
-       "nosuid",
-       "private",
-       "rec",
-       "relatime",
-       "remount",
-       "shared",
-       "slave",
-       "strictatime",
-       "suid",
-       "unbindable",
-};
-
 /*
  * Check for a common mount option that manipulates s_flags.
  */
 static int vfs_parse_sb_flag(struct fs_context *fc, const char *key)
 {
        unsigned int token;
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(forbidden_sb_flag); i++)
-               if (strcmp(key, forbidden_sb_flag[i]) == 0)
-                       return -EINVAL;
 
        token = lookup_constant(common_set_sb_flag, key, 0);
        if (token) {
index 506c5e643f0d9b066f10c0668e8666fe737366d8..5e796e6c38e56146a87cf9c0fabc892f7506e5d3 100644 (file)
@@ -8,7 +8,7 @@ config FSCACHE
          Different sorts of caches can be plugged in, depending on the
          resources available.
 
-         See Documentation/filesystems/caching/fscache.txt for more information.
+         See Documentation/filesystems/caching/fscache.rst for more information.
 
 config FSCACHE_STATS
        bool "Gather statistical information on local caching"
@@ -25,7 +25,7 @@ config FSCACHE_STATS
          between CPUs.  On the other hand, the stats are very useful for
          debugging purposes.  Saying 'Y' here is recommended.
 
-         See Documentation/filesystems/caching/fscache.txt for more information.
+         See Documentation/filesystems/caching/fscache.rst for more information.
 
 config FSCACHE_HISTOGRAM
        bool "Gather latency information on local caching"
@@ -42,7 +42,7 @@ config FSCACHE_HISTOGRAM
          bouncing between CPUs.  On the other hand, the histogram may be
          useful for debugging purposes.  Saying 'N' here is recommended.
 
-         See Documentation/filesystems/caching/fscache.txt for more information.
+         See Documentation/filesystems/caching/fscache.rst for more information.
 
 config FSCACHE_DEBUG
        bool "Debug FS-Cache"
@@ -52,7 +52,7 @@ config FSCACHE_DEBUG
          management module.  If this is set, the debugging output may be
          enabled by setting bits in /sys/modules/fscache/parameter/debug.
 
-         See Documentation/filesystems/caching/fscache.txt for more information.
+         See Documentation/filesystems/caching/fscache.rst for more information.
 
 config FSCACHE_OBJECT_LIST
        bool "Maintain global object list for debugging purposes"
index f78793f3d21e16940a18148b8026a52d2bc9905c..fcc136361415e7980dc887f563cf18c08b05bd30 100644 (file)
@@ -172,7 +172,7 @@ no_preference:
  *
  * Initialise a record of a cache and fill in the name.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 void fscache_init_cache(struct fscache_cache *cache,
@@ -207,7 +207,7 @@ EXPORT_SYMBOL(fscache_init_cache);
  *
  * Add a cache to the system, making it available for netfs's to use.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 int fscache_add_cache(struct fscache_cache *cache,
@@ -307,7 +307,7 @@ EXPORT_SYMBOL(fscache_add_cache);
  * Note that an I/O error occurred in a cache and that it should no longer be
  * used for anything.  This also reports the error into the kernel log.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 void fscache_io_error(struct fscache_cache *cache)
@@ -355,7 +355,7 @@ static void fscache_withdraw_all_objects(struct fscache_cache *cache,
  * Withdraw a cache from service, unbinding all its cache objects from the
  * netfs cookies they're currently representing.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 void fscache_withdraw_cache(struct fscache_cache *cache)
index 0ce39658a6200605a7cb00ad60eb86178929946b..751bc5b1cddf952d34bcc16d836b3776accf724e 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
- * See Documentation/filesystems/caching/netfs-api.txt for more information on
+ * See Documentation/filesystems/caching/netfs-api.rst for more information on
  * the netfs API.
  */
 
index cfeba839a0f2f1620ea4d726b5b847e54be84910..cb2146e02cd5442637ed50ccacc6071aab30a9dd 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
- * See Documentation/filesystems/caching/object.txt for a description of the
+ * See Documentation/filesystems/caching/object.rst for a description of the
  * object state machine and the in-kernel representations.
  */
 
@@ -295,7 +295,7 @@ static void fscache_object_work_func(struct work_struct *work)
  *
  * Initialise a cache object description to its basic values.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 void fscache_object_init(struct fscache_object *object,
index 1a22a55f75a0934064f11a326693e7ed8ad5a715..4a5651d4904e5fa43fd959619ca560194b4fc166 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
- * See Documentation/filesystems/caching/operations.txt
+ * See Documentation/filesystems/caching/operations.rst
  */
 
 #define FSCACHE_DEBUG_LEVEL OPERATION
index eb2a585572dc72f76c997ef042eb6d8ee493fe5e..774b2618018a6a87fa3c9514da1cfecda1be2aa9 100644 (file)
@@ -12,7 +12,7 @@ config FUSE_FS
          although chances are your distribution already has that library
          installed if you've installed the "fuse" package itself.
 
-         See <file:Documentation/filesystems/fuse.txt> for more information.
+         See <file:Documentation/filesystems/fuse.rst> for more information.
          See <file:Documentation/Changes> for needed library/utility version.
 
          If you want to develop a userspace FS, or if you want to use
index 97eec7522bf203a929cefd7e0afd67fdd87cd7cb..c7a65cf2bccaf0ae825389dce154679906365373 100644 (file)
@@ -2081,7 +2081,7 @@ static void end_polls(struct fuse_conn *fc)
  * The same effect is usually achievable through killing the filesystem daemon
  * and all users of the filesystem.  The exception is the combination of an
  * asynchronous request and the tricky deadlock (see
- * Documentation/filesystems/fuse.txt).
+ * Documentation/filesystems/fuse.rst).
  *
  * Aborting requests under I/O goes as follows: 1: Separate out unlocked
  * requests, they should be finished off immediately.  Locked requests will be
index 9d67b830fb7a25445bfd4f930c6ef7fa66fbbd1e..bac51c32d660263112182c7fdeb8c917b3c33455 100644 (file)
@@ -915,84 +915,40 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file)
        fuse_readpages_end(fc, &ap->args, err);
 }
 
-struct fuse_fill_data {
-       struct fuse_io_args *ia;
-       struct file *file;
-       struct inode *inode;
-       unsigned int nr_pages;
-       unsigned int max_pages;
-};
-
-static int fuse_readpages_fill(void *_data, struct page *page)
+static void fuse_readahead(struct readahead_control *rac)
 {
-       struct fuse_fill_data *data = _data;
-       struct fuse_io_args *ia = data->ia;
-       struct fuse_args_pages *ap = &ia->ap;
-       struct inode *inode = data->inode;
+       struct inode *inode = rac->mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
+       unsigned int i, max_pages, nr_pages = 0;
 
-       fuse_wait_on_page_writeback(inode, page->index);
-
-       if (ap->num_pages &&
-           (ap->num_pages == fc->max_pages ||
-            (ap->num_pages + 1) * PAGE_SIZE > fc->max_read ||
-            ap->pages[ap->num_pages - 1]->index + 1 != page->index)) {
-               data->max_pages = min_t(unsigned int, data->nr_pages,
-                                       fc->max_pages);
-               fuse_send_readpages(ia, data->file);
-               data->ia = ia = fuse_io_alloc(NULL, data->max_pages);
-               if (!ia) {
-                       unlock_page(page);
-                       return -ENOMEM;
-               }
-               ap = &ia->ap;
-       }
-
-       if (WARN_ON(ap->num_pages >= data->max_pages)) {
-               unlock_page(page);
-               fuse_io_free(ia);
-               return -EIO;
-       }
-
-       get_page(page);
-       ap->pages[ap->num_pages] = page;
-       ap->descs[ap->num_pages].length = PAGE_SIZE;
-       ap->num_pages++;
-       data->nr_pages--;
-       return 0;
-}
-
-static int fuse_readpages(struct file *file, struct address_space *mapping,
-                         struct list_head *pages, unsigned nr_pages)
-{
-       struct inode *inode = mapping->host;
-       struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_fill_data data;
-       int err;
-
-       err = -EIO;
        if (is_bad_inode(inode))
-               goto out;
+               return;
 
-       data.file = file;
-       data.inode = inode;
-       data.nr_pages = nr_pages;
-       data.max_pages = min_t(unsigned int, nr_pages, fc->max_pages);
-;
-       data.ia = fuse_io_alloc(NULL, data.max_pages);
-       err = -ENOMEM;
-       if (!data.ia)
-               goto out;
+       max_pages = min_t(unsigned int, fc->max_pages,
+                       fc->max_read / PAGE_SIZE);
 
-       err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
-       if (!err) {
-               if (data.ia->ap.num_pages)
-                       fuse_send_readpages(data.ia, file);
-               else
-                       fuse_io_free(data.ia);
+       for (;;) {
+               struct fuse_io_args *ia;
+               struct fuse_args_pages *ap;
+
+               nr_pages = readahead_count(rac) - nr_pages;
+               if (nr_pages > max_pages)
+                       nr_pages = max_pages;
+               if (nr_pages == 0)
+                       break;
+               ia = fuse_io_alloc(NULL, nr_pages);
+               if (!ia)
+                       return;
+               ap = &ia->ap;
+               nr_pages = __readahead_batch(rac, ap->pages, nr_pages);
+               for (i = 0; i < nr_pages; i++) {
+                       fuse_wait_on_page_writeback(inode,
+                                                   readahead_index(rac) + i);
+                       ap->descs[i].length = PAGE_SIZE;
+               }
+               ap->num_pages = nr_pages;
+               fuse_send_readpages(ia, rac->file);
        }
-out:
-       return err;
 }
 
 static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
@@ -3373,10 +3329,10 @@ static const struct file_operations fuse_file_operations = {
 
 static const struct address_space_operations fuse_file_aops  = {
        .readpage       = fuse_readpage,
+       .readahead      = fuse_readahead,
        .writepage      = fuse_writepage,
        .writepages     = fuse_writepages,
        .launder_page   = fuse_launder_page,
-       .readpages      = fuse_readpages,
        .set_page_dirty = __set_page_dirty_nobuffers,
        .bmap           = fuse_bmap,
        .direct_IO      = fuse_direct_IO,
index 786c1ce8f03051130ec55a7ac2a7970e3ebe97cf..72c9560f4467e0ae96da63545876f46971d288d1 100644 (file)
@@ -577,7 +577,7 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos,
 }
 
 /**
- * gfs2_readpages - Read a bunch of pages at once
+ * gfs2_readahead - Read a bunch of pages at once
  * @file: The file to read from
  * @mapping: Address space info
  * @pages: List of pages to read
@@ -590,31 +590,24 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos,
  *    obviously not something we'd want to do on too regular a basis.
  *    Any I/O we ignore at this time will be done via readpage later.
  * 2. We don't handle stuffed files here we let readpage do the honours.
- * 3. mpage_readpages() does most of the heavy lifting in the common case.
+ * 3. mpage_readahead() does most of the heavy lifting in the common case.
  * 4. gfs2_block_map() is relied upon to set BH_Boundary in the right places.
  */
 
-static int gfs2_readpages(struct file *file, struct address_space *mapping,
-                         struct list_head *pages, unsigned nr_pages)
+static void gfs2_readahead(struct readahead_control *rac)
 {
-       struct inode *inode = mapping->host;
+       struct inode *inode = rac->mapping->host;
        struct gfs2_inode *ip = GFS2_I(inode);
-       struct gfs2_sbd *sdp = GFS2_SB(inode);
        struct gfs2_holder gh;
-       int ret;
 
        gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
-       ret = gfs2_glock_nq(&gh);
-       if (unlikely(ret))
+       if (gfs2_glock_nq(&gh))
                goto out_uninit;
        if (!gfs2_is_stuffed(ip))
-               ret = mpage_readpages(mapping, pages, nr_pages, gfs2_block_map);
+               mpage_readahead(rac, gfs2_block_map);
        gfs2_glock_dq(&gh);
 out_uninit:
        gfs2_holder_uninit(&gh);
-       if (unlikely(gfs2_withdrawn(sdp)))
-               ret = -EIO;
-       return ret;
 }
 
 /**
@@ -833,7 +826,7 @@ static const struct address_space_operations gfs2_aops = {
        .writepage = gfs2_writepage,
        .writepages = gfs2_writepages,
        .readpage = gfs2_readpage,
-       .readpages = gfs2_readpages,
+       .readahead = gfs2_readahead,
        .bmap = gfs2_bmap,
        .invalidatepage = gfs2_invalidatepage,
        .releasepage = gfs2_releasepage,
@@ -847,7 +840,7 @@ static const struct address_space_operations gfs2_jdata_aops = {
        .writepage = gfs2_jdata_writepage,
        .writepages = gfs2_jdata_writepages,
        .readpage = gfs2_readpage,
-       .readpages = gfs2_readpages,
+       .readahead = gfs2_readahead,
        .set_page_dirty = jdata_set_page_dirty,
        .bmap = gfs2_bmap,
        .invalidatepage = gfs2_invalidatepage,
index c3f7732415be821253704aa893aace2f110ef033..c0f2875c946c9cdb0f601768876bcaa43afbe733 100644 (file)
@@ -354,7 +354,7 @@ static __be64 *gfs2_dir_get_hash_table(struct gfs2_inode *ip)
 
        hc = kmalloc(hsize, GFP_NOFS | __GFP_NOWARN);
        if (hc == NULL)
-               hc = __vmalloc(hsize, GFP_NOFS, PAGE_KERNEL);
+               hc = __vmalloc(hsize, GFP_NOFS);
 
        if (hc == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1166,7 +1166,7 @@ static int dir_double_exhash(struct gfs2_inode *dip)
 
        hc2 = kmalloc_array(hsize_bytes, 2, GFP_NOFS | __GFP_NOWARN);
        if (hc2 == NULL)
-               hc2 = __vmalloc(hsize_bytes * 2, GFP_NOFS, PAGE_KERNEL);
+               hc2 = __vmalloc(hsize_bytes * 2, GFP_NOFS);
 
        if (!hc2)
                return -ENOMEM;
@@ -1327,7 +1327,7 @@ static void *gfs2_alloc_sort_buffer(unsigned size)
        if (size < KMALLOC_MAX_SIZE)
                ptr = kmalloc(size, GFP_NOFS | __GFP_NOWARN);
        if (!ptr)
-               ptr = __vmalloc(size, GFP_NOFS, PAGE_KERNEL);
+               ptr = __vmalloc(size, GFP_NOFS);
        return ptr;
 }
 
@@ -1987,8 +1987,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
 
        ht = kzalloc(size, GFP_NOFS | __GFP_NOWARN);
        if (ht == NULL)
-               ht = __vmalloc(size, GFP_NOFS | __GFP_NOWARN | __GFP_ZERO,
-                              PAGE_KERNEL);
+               ht = __vmalloc(size, GFP_NOFS | __GFP_NOWARN | __GFP_ZERO);
        if (!ht)
                return -ENOMEM;
 
index 48b54ec1c79316910d6114053e4a24cf6c7de264..cb2a11b458c66beeb96e7b1fe9fc850f29f54fe8 100644 (file)
@@ -509,12 +509,12 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head,
        unsigned int bsize = sdp->sd_sb.sb_bsize, off;
        unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
        unsigned int shift = PAGE_SHIFT - bsize_shift;
-       unsigned int max_bio_size = 2 * 1024 * 1024;
+       unsigned int max_blocks = 2 * 1024 * 1024 >> bsize_shift;
        struct gfs2_journal_extent *je;
        int sz, ret = 0;
        struct bio *bio = NULL;
        struct page *page = NULL;
-       bool bio_chained = false, done = false;
+       bool done = false;
        errseq_t since;
 
        memset(head, 0, sizeof(*head));
@@ -537,10 +537,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head,
                                off = 0;
                        }
 
-                       if (!bio || (bio_chained && !off) ||
-                           bio->bi_iter.bi_size >= max_bio_size) {
-                               /* start new bio */
-                       } else {
+                       if (bio && (off || block < blocks_submitted + max_blocks)) {
                                sector_t sector = dblock << sdp->sd_fsb2bb_shift;
 
                                if (bio_end_sector(bio) == sector) {
@@ -553,19 +550,17 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head,
                                                (PAGE_SIZE - off) >> bsize_shift;
 
                                        bio = gfs2_chain_bio(bio, blocks);
-                                       bio_chained = true;
                                        goto add_block_to_new_bio;
                                }
                        }
 
                        if (bio) {
-                               blocks_submitted = block + 1;
+                               blocks_submitted = block;
                                submit_bio(bio);
                        }
 
                        bio = gfs2_log_alloc_bio(sdp, dblock, gfs2_end_log_read);
                        bio->bi_opf = REQ_OP_READ;
-                       bio_chained = false;
 add_block_to_new_bio:
                        sz = bio_add_page(bio, page, bsize, off);
                        BUG_ON(sz != bsize);
@@ -573,7 +568,7 @@ block_added:
                        off += bsize;
                        if (off == PAGE_SIZE)
                                page = NULL;
-                       if (blocks_submitted < 2 * max_bio_size >> bsize_shift) {
+                       if (blocks_submitted <= blocks_read + max_blocks) {
                                /* Keep at least one bio in flight */
                                continue;
                        }
index 8259fef3f9863be547ef4241a37a46283668a710..4b67d47a7e00467b087a53f3d44cb15bffee9248 100644 (file)
@@ -1365,7 +1365,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp)
        sdp->sd_quota_bitmap = kzalloc(bm_size, GFP_NOFS | __GFP_NOWARN);
        if (sdp->sd_quota_bitmap == NULL)
                sdp->sd_quota_bitmap = __vmalloc(bm_size, GFP_NOFS |
-                                                __GFP_ZERO, PAGE_KERNEL);
+                                                __GFP_ZERO);
        if (!sdp->sd_quota_bitmap)
                return error;
 
index 44f6e89bcb751cb5706e6e3cd693f85c4c59b2e4..129926b5142d8f8b2626fbc5d86a86499d5329f5 100644 (file)
@@ -6,7 +6,7 @@ config HFS_FS
        help
          If you say Y here, you will be able to mount Macintosh-formatted
          floppy disks and hard drive partitions with full read-write access.
-         Please read <file:Documentation/filesystems/hfs.txt> to learn about
+         Please read <file:Documentation/filesystems/hfs.rst> to learn about
          the available mount options.
 
          To compile this file system support as a module, choose M here: the
index 56aa0336254ab1e0ec4d79c05ee9c22277bb2522..2b36dc6f0a108c4647b95625cc18c734e5498e24 100644 (file)
@@ -9,7 +9,7 @@ config HPFS_FS
          write files to an OS/2 HPFS partition on your hard drive. OS/2
          floppies however are in regular MSDOS format, so you don't need this
          option in order to be able to read them. Read
-         <file:Documentation/filesystems/hpfs.txt>.
+         <file:Documentation/filesystems/hpfs.rst>.
 
          To compile this file system support as a module, choose M here: the
          module will be called hpfs.  If unsure, say N.
index b36abf9cb345a513bf79d8765fda2a44dae1c91a..2de0d3492d15a9639e774d85464757deca226e55 100644 (file)
@@ -125,10 +125,9 @@ static int hpfs_writepage(struct page *page, struct writeback_control *wbc)
        return block_write_full_page(page, hpfs_get_block, wbc);
 }
 
-static int hpfs_readpages(struct file *file, struct address_space *mapping,
-                         struct list_head *pages, unsigned nr_pages)
+static void hpfs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, hpfs_get_block);
+       mpage_readahead(rac, hpfs_get_block);
 }
 
 static int hpfs_writepages(struct address_space *mapping,
@@ -198,7 +197,7 @@ static int hpfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 const struct address_space_operations hpfs_aops = {
        .readpage = hpfs_readpage,
        .writepage = hpfs_writepage,
-       .readpages = hpfs_readpages,
+       .readahead = hpfs_readahead,
        .writepages = hpfs_writepages,
        .write_begin = hpfs_write_begin,
        .write_end = hpfs_write_end,
index 93d9252a00ab4b2ed0fbb274b28e1df468aa71ca..37226a9cfa4fd33b73cf0aeba6ed20ce4a50a7c7 100644 (file)
@@ -1606,14 +1606,14 @@ EXPORT_SYMBOL(iput);
  *     @inode:  inode owning the block number being requested
  *     @block: pointer containing the block to find
  *
- *     Replaces the value in *block with the block number on the device holding
+ *     Replaces the value in ``*block`` with the block number on the device holding
  *     corresponding to the requested block number in the file.
  *     That is, asked for block 4 of inode 1 the function will replace the
- *     4 in *block, with disk block relative to the disk start that holds that
+ *     4 in ``*block``, with disk block relative to the disk start that holds that
  *     block of the file.
  *
  *     Returns -EINVAL in case of error, 0 otherwise. If mapping falls into a
- *     hole, returns 0 and *block is also set to 0.
+ *     hole, returns 0 and ``*block`` is also set to 0.
  */
 int bmap(struct inode *inode, sector_t *block)
 {
index aa5d45524e87da09d728abe6c7868bce8fb4d030..0d467e32dd7e90a5f3f542d177e98bb331b57f8e 100644 (file)
@@ -126,7 +126,6 @@ extern struct open_how build_open_how(int flags, umode_t mode);
 extern int build_open_flags(const struct open_how *how, struct open_flags *op);
 
 long do_sys_ftruncate(unsigned int fd, loff_t length, int small);
-long do_faccessat(int dfd, const char __user *filename, int mode);
 int do_fchmodat(int dfd, const char __user *filename, umode_t mode);
 int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group,
                int flag);
index 89e21961d1adf9f7c6d4fe730e6a822b74c5b206..a1ed7620fbac3e5dd9a59d8ba9f535ba5994221a 100644 (file)
@@ -59,24 +59,19 @@ iomap_page_create(struct inode *inode, struct page *page)
         * migrate_page_move_mapping() assumes that pages with private data have
         * their count elevated by 1.
         */
-       get_page(page);
-       set_page_private(page, (unsigned long)iop);
-       SetPagePrivate(page);
+       attach_page_private(page, iop);
        return iop;
 }
 
 static void
 iomap_page_release(struct page *page)
 {
-       struct iomap_page *iop = to_iomap_page(page);
+       struct iomap_page *iop = detach_page_private(page);
 
        if (!iop)
                return;
        WARN_ON_ONCE(atomic_read(&iop->read_count));
        WARN_ON_ONCE(atomic_read(&iop->write_count));
-       ClearPagePrivate(page);
-       set_page_private(page, 0);
-       put_page(page);
        kfree(iop);
 }
 
@@ -214,9 +209,8 @@ iomap_read_end_io(struct bio *bio)
 struct iomap_readpage_ctx {
        struct page             *cur_page;
        bool                    cur_page_in_bio;
-       bool                    is_readahead;
        struct bio              *bio;
-       struct list_head        *pages;
+       struct readahead_control *rac;
 };
 
 static void
@@ -308,7 +302,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
                if (ctx->bio)
                        submit_bio(ctx->bio);
 
-               if (ctx->is_readahead) /* same as readahead_gfp_mask */
+               if (ctx->rac) /* same as readahead_gfp_mask */
                        gfp |= __GFP_NORETRY | __GFP_NOWARN;
                ctx->bio = bio_alloc(gfp, min(BIO_MAX_PAGES, nr_vecs));
                /*
@@ -319,7 +313,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
                if (!ctx->bio)
                        ctx->bio = bio_alloc(orig_gfp, 1);
                ctx->bio->bi_opf = REQ_OP_READ;
-               if (ctx->is_readahead)
+               if (ctx->rac)
                        ctx->bio->bi_opf |= REQ_RAHEAD;
                ctx->bio->bi_iter.bi_sector = sector;
                bio_set_dev(ctx->bio, iomap->bdev);
@@ -367,7 +361,7 @@ iomap_readpage(struct page *page, const struct iomap_ops *ops)
        }
 
        /*
-        * Just like mpage_readpages and block_read_full_page we always
+        * Just like mpage_readahead and block_read_full_page we always
         * return 0 and just mark the page as PageError on errors.  This
         * should be cleaned up all through the stack eventually.
         */
@@ -375,36 +369,8 @@ iomap_readpage(struct page *page, const struct iomap_ops *ops)
 }
 EXPORT_SYMBOL_GPL(iomap_readpage);
 
-static struct page *
-iomap_next_page(struct inode *inode, struct list_head *pages, loff_t pos,
-               loff_t length, loff_t *done)
-{
-       while (!list_empty(pages)) {
-               struct page *page = lru_to_page(pages);
-
-               if (page_offset(page) >= (u64)pos + length)
-                       break;
-
-               list_del(&page->lru);
-               if (!add_to_page_cache_lru(page, inode->i_mapping, page->index,
-                               GFP_NOFS))
-                       return page;
-
-               /*
-                * If we already have a page in the page cache at index we are
-                * done.  Upper layers don't care if it is uptodate after the
-                * readpages call itself as every page gets checked again once
-                * actually needed.
-                */
-               *done += PAGE_SIZE;
-               put_page(page);
-       }
-
-       return NULL;
-}
-
 static loff_t
-iomap_readpages_actor(struct inode *inode, loff_t pos, loff_t length,
+iomap_readahead_actor(struct inode *inode, loff_t pos, loff_t length,
                void *data, struct iomap *iomap, struct iomap *srcmap)
 {
        struct iomap_readpage_ctx *ctx = data;
@@ -418,10 +384,7 @@ iomap_readpages_actor(struct inode *inode, loff_t pos, loff_t length,
                        ctx->cur_page = NULL;
                }
                if (!ctx->cur_page) {
-                       ctx->cur_page = iomap_next_page(inode, ctx->pages,
-                                       pos, length, &done);
-                       if (!ctx->cur_page)
-                               break;
+                       ctx->cur_page = readahead_page(ctx->rac);
                        ctx->cur_page_in_bio = false;
                }
                ret = iomap_readpage_actor(inode, pos + done, length - done,
@@ -431,32 +394,43 @@ iomap_readpages_actor(struct inode *inode, loff_t pos, loff_t length,
        return done;
 }
 
-int
-iomap_readpages(struct address_space *mapping, struct list_head *pages,
-               unsigned nr_pages, const struct iomap_ops *ops)
+/**
+ * iomap_readahead - Attempt to read pages from a file.
+ * @rac: Describes the pages to be read.
+ * @ops: The operations vector for the filesystem.
+ *
+ * This function is for filesystems to call to implement their readahead
+ * address_space operation.
+ *
+ * Context: The @ops callbacks may submit I/O (eg to read the addresses of
+ * blocks from disc), and may wait for it.  The caller may be trying to
+ * access a different page, and so sleeping excessively should be avoided.
+ * It may allocate memory, but should avoid costly allocations.  This
+ * function is called with memalloc_nofs set, so allocations will not cause
+ * the filesystem to be reentered.
+ */
+void iomap_readahead(struct readahead_control *rac, const struct iomap_ops *ops)
 {
+       struct inode *inode = rac->mapping->host;
+       loff_t pos = readahead_pos(rac);
+       loff_t length = readahead_length(rac);
        struct iomap_readpage_ctx ctx = {
-               .pages          = pages,
-               .is_readahead   = true,
+               .rac    = rac,
        };
-       loff_t pos = page_offset(list_entry(pages->prev, struct page, lru));
-       loff_t last = page_offset(list_entry(pages->next, struct page, lru));
-       loff_t length = last - pos + PAGE_SIZE, ret = 0;
 
-       trace_iomap_readpages(mapping->host, nr_pages);
+       trace_iomap_readahead(inode, readahead_count(rac));
 
        while (length > 0) {
-               ret = iomap_apply(mapping->host, pos, length, 0, ops,
-                               &ctx, iomap_readpages_actor);
+               loff_t ret = iomap_apply(inode, pos, length, 0, ops,
+                               &ctx, iomap_readahead_actor);
                if (ret <= 0) {
                        WARN_ON_ONCE(ret == 0);
-                       goto done;
+                       break;
                }
                pos += ret;
                length -= ret;
        }
-       ret = 0;
-done:
+
        if (ctx.bio)
                submit_bio(ctx.bio);
        if (ctx.cur_page) {
@@ -464,15 +438,8 @@ done:
                        unlock_page(ctx.cur_page);
                put_page(ctx.cur_page);
        }
-
-       /*
-        * Check that we didn't lose a page due to the arcance calling
-        * conventions..
-        */
-       WARN_ON_ONCE(!ret && !list_empty(ctx.pages));
-       return ret;
 }
-EXPORT_SYMBOL_GPL(iomap_readpages);
+EXPORT_SYMBOL_GPL(iomap_readahead);
 
 /*
  * iomap_is_partially_uptodate checks whether blocks within a page are
@@ -554,14 +521,8 @@ iomap_migrate_page(struct address_space *mapping, struct page *newpage,
        if (ret != MIGRATEPAGE_SUCCESS)
                return ret;
 
-       if (page_has_private(page)) {
-               ClearPagePrivate(page);
-               get_page(newpage);
-               set_page_private(newpage, page_private(page));
-               set_page_private(page, 0);
-               put_page(page);
-               SetPagePrivate(newpage);
-       }
+       if (page_has_private(page))
+               attach_page_private(newpage, detach_page_private(page));
 
        if (mode != MIGRATE_SYNC_NO_COPY)
                migrate_page_copy(newpage, page);
index 4df19c66f597734b0e816b227f597731646f8e9d..5693a39d52fb630edf688ef58426c782632be759 100644 (file)
@@ -39,7 +39,7 @@ DEFINE_EVENT(iomap_readpage_class, name,      \
        TP_PROTO(struct inode *inode, int nr_pages), \
        TP_ARGS(inode, nr_pages))
 DEFINE_READPAGE_EVENT(iomap_readpage);
-DEFINE_READPAGE_EVENT(iomap_readpages);
+DEFINE_READPAGE_EVENT(iomap_readahead);
 
 DECLARE_EVENT_CLASS(iomap_range_class,
        TP_PROTO(struct inode *inode, unsigned long off, unsigned int len),
index 5e7419599f50293401861a8ffbdb12f10c65c2b5..08ffd37b9bb8f65ea186881e519ca03638884422 100644 (file)
@@ -8,7 +8,7 @@ config ISO9660_FS
          long Unix filenames and symbolic links are also supported by this
          driver.  If you have a CD-ROM drive and want to do more with it than
          just listen to audio CDs and watch its LEDs, say Y (and read
-         <file:Documentation/filesystems/isofs.txt> and the CD-ROM-HOWTO,
+         <file:Documentation/filesystems/isofs.rst> and the CD-ROM-HOWTO,
          available from <http://www.tldp.org/docs.html#howto>), thereby
          enlarging your kernel by about 27 KB; otherwise say N.
 
index 62c0462dc89f3e52d88edb162717d80091c239a5..95b1f377ad090868c5b633937f3776bd62285cdd 100644 (file)
@@ -1185,10 +1185,9 @@ static int isofs_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, isofs_get_block);
 }
 
-static int isofs_readpages(struct file *file, struct address_space *mapping,
-                       struct list_head *pages, unsigned nr_pages)
+static void isofs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, isofs_get_block);
+       mpage_readahead(rac, isofs_get_block);
 }
 
 static sector_t _isofs_bmap(struct address_space *mapping, sector_t block)
@@ -1198,7 +1197,7 @@ static sector_t _isofs_bmap(struct address_space *mapping, sector_t block)
 
 static const struct address_space_operations isofs_aops = {
        .readpage = isofs_readpage,
-       .readpages = isofs_readpages,
+       .readahead = isofs_readahead,
        .bmap = _isofs_bmap
 };
 
index 9486afcdac76fbef84801784121aad956a0584ff..6f65bfa9f18d5b629b82c3efc349107e6d2dbecf 100644 (file)
@@ -296,10 +296,9 @@ static int jfs_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, jfs_get_block);
 }
 
-static int jfs_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned nr_pages)
+static void jfs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, jfs_get_block);
+       mpage_readahead(rac, jfs_get_block);
 }
 
 static void jfs_write_failed(struct address_space *mapping, loff_t to)
@@ -358,7 +357,7 @@ static ssize_t jfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 
 const struct address_space_operations jfs_aops = {
        .readpage       = jfs_readpage,
-       .readpages      = jfs_readpages,
+       .readahead      = jfs_readahead,
        .writepage      = jfs_writepage,
        .writepages     = jfs_writepages,
        .write_begin    = jfs_write_begin,
index b8a31c1c4fff3c2a4e35c57a953d9bf025569a4b..1d4f4d5da704832759a9257a7b43f9065979aef5 100644 (file)
@@ -61,7 +61,7 @@
  *
  *  Initial implementation of mandatory locks. SunOS turned out to be
  *  a rotten model, so I implemented the "obvious" semantics.
- *  See 'Documentation/filesystems/mandatory-locking.txt' for details.
+ *  See 'Documentation/filesystems/mandatory-locking.rst' for details.
  *  Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
  *
  *  Don't allow mandatory locks on mmap()'ed files. Added simple functions to
index 711a4093e475e9fdf9fb8df1d81ab289ee33a498..c7abb7b394d8eed9ca2833e656d345fc44e3a402 100644 (file)
@@ -9,7 +9,13 @@ struct mnt_namespace {
        atomic_t                count;
        struct ns_common        ns;
        struct mount *  root;
+       /*
+        * Traversal and modification of .list is protected by either
+        * - taking namespace_sem for write, OR
+        * - taking namespace_sem for read AND taking .ns_lock.
+        */
        struct list_head        list;
+       spinlock_t              ns_lock;
        struct user_namespace   *user_ns;
        struct ucounts          *ucounts;
        u64                     seq;    /* Sequence number to prevent loops */
@@ -133,9 +139,7 @@ struct proc_mounts {
        struct mnt_namespace *ns;
        struct path root;
        int (*show)(struct seq_file *, struct vfsmount *);
-       void *cached_mount;
-       u64 cached_event;
-       loff_t cached_index;
+       struct mount cursor;
 };
 
 extern const struct seq_operations mounts_op;
@@ -153,3 +157,5 @@ static inline bool is_anon_ns(struct mnt_namespace *ns)
 {
        return ns->seq == 0;
 }
+
+extern void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor);
index ccba3c4c44797b54b859acc2378d5f317e6c29be..830e6cc2a9e727ec37d29ad743fc4e2f8cebe2fe 100644 (file)
@@ -91,7 +91,7 @@ mpage_alloc(struct block_device *bdev,
 }
 
 /*
- * support function for mpage_readpages.  The fs supplied get_block might
+ * support function for mpage_readahead.  The fs supplied get_block might
  * return an up to date buffer.  This is used to map that buffer into
  * the page, which allows readpage to avoid triggering a duplicate call
  * to get_block.
@@ -338,13 +338,8 @@ confused:
 }
 
 /**
- * mpage_readpages - populate an address space with some pages & start reads against them
- * @mapping: the address_space
- * @pages: The address of a list_head which contains the target pages.  These
- *   pages have their ->index populated and are otherwise uninitialised.
- *   The page at @pages->prev has the lowest file offset, and reads should be
- *   issued in @pages->prev to @pages->next order.
- * @nr_pages: The number of pages at *@pages
+ * mpage_readahead - start reads against pages
+ * @rac: Describes which pages to read.
  * @get_block: The filesystem's block mapper function.
  *
  * This function walks the pages and the blocks within each page, building and
@@ -381,36 +376,25 @@ confused:
  *
  * This all causes the disk requests to be issued in the correct order.
  */
-int
-mpage_readpages(struct address_space *mapping, struct list_head *pages,
-                               unsigned nr_pages, get_block_t get_block)
+void mpage_readahead(struct readahead_control *rac, get_block_t get_block)
 {
+       struct page *page;
        struct mpage_readpage_args args = {
                .get_block = get_block,
                .is_readahead = true,
        };
-       unsigned page_idx;
-
-       for (page_idx = 0; page_idx < nr_pages; page_idx++) {
-               struct page *page = lru_to_page(pages);
 
+       while ((page = readahead_page(rac))) {
                prefetchw(&page->flags);
-               list_del(&page->lru);
-               if (!add_to_page_cache_lru(page, mapping,
-                                       page->index,
-                                       readahead_gfp_mask(mapping))) {
-                       args.page = page;
-                       args.nr_pages = nr_pages - page_idx;
-                       args.bio = do_mpage_readpage(&args);
-               }
+               args.page = page;
+               args.nr_pages = readahead_count(rac);
+               args.bio = do_mpage_readpage(&args);
                put_page(page);
        }
-       BUG_ON(!list_empty(pages));
        if (args.bio)
                mpage_bio_submit(REQ_OP_READ, REQ_RAHEAD, args.bio);
-       return 0;
 }
-EXPORT_SYMBOL(mpage_readpages);
+EXPORT_SYMBOL(mpage_readahead);
 
 /*
  * This isn't called much at all
@@ -563,7 +547,7 @@ static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
                 * Page has buffers, but they are all unmapped. The page was
                 * created by pagein or read over a hole which was handled by
                 * block_read_full_page().  If this address_space is also
-                * using mpage_readpages then this can rarely happen.
+                * using mpage_readahead then this can rarely happen.
                 */
                goto confused;
        }
index a320371899cf9a9fab82d0f3de7272f03414f820..d81f73ff1a8ba45e615c22ec4b31b4558b3231a5 100644 (file)
@@ -3505,12 +3505,14 @@ EXPORT_SYMBOL(user_path_create);
 
 int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
 {
+       bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
        int error = may_create(dir, dentry);
 
        if (error)
                return error;
 
-       if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+       if ((S_ISCHR(mode) || S_ISBLK(mode)) && !is_whiteout &&
+           !capable(CAP_MKNOD))
                return -EPERM;
 
        if (!dir->i_op->mknod)
@@ -4345,9 +4347,6 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
            (flags & RENAME_EXCHANGE))
                return -EINVAL;
 
-       if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
-               return -EPERM;
-
        if (flags & RENAME_EXCHANGE)
                target_flags = 0;
 
@@ -4483,20 +4482,6 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
        return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
 }
 
-int vfs_whiteout(struct inode *dir, struct dentry *dentry)
-{
-       int error = may_create(dir, dentry);
-       if (error)
-               return error;
-
-       if (!dir->i_op->mknod)
-               return -EPERM;
-
-       return dir->i_op->mknod(dir, dentry,
-                               S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
-}
-EXPORT_SYMBOL(vfs_whiteout);
-
 int readlink_copy(char __user *buffer, int buflen, const char *link)
 {
        int len = PTR_ERR(link);
index a28e4db075ede216f684561fbc348a01670560a9..a6baee3c790460e94c27250b712a4e2a465cf642 100644 (file)
@@ -648,6 +648,21 @@ struct vfsmount *lookup_mnt(const struct path *path)
        return m;
 }
 
+static inline void lock_ns_list(struct mnt_namespace *ns)
+{
+       spin_lock(&ns->ns_lock);
+}
+
+static inline void unlock_ns_list(struct mnt_namespace *ns)
+{
+       spin_unlock(&ns->ns_lock);
+}
+
+static inline bool mnt_is_cursor(struct mount *mnt)
+{
+       return mnt->mnt.mnt_flags & MNT_CURSOR;
+}
+
 /*
  * __is_local_mountpoint - Test to see if dentry is a mountpoint in the
  *                         current mount namespace.
@@ -673,11 +688,15 @@ bool __is_local_mountpoint(struct dentry *dentry)
                goto out;
 
        down_read(&namespace_sem);
+       lock_ns_list(ns);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
+               if (mnt_is_cursor(mnt))
+                       continue;
                is_covered = (mnt->mnt_mountpoint == dentry);
                if (is_covered)
                        break;
        }
+       unlock_ns_list(ns);
        up_read(&namespace_sem);
 out:
        return is_covered;
@@ -1245,46 +1264,71 @@ struct vfsmount *mnt_clone_internal(const struct path *path)
 }
 
 #ifdef CONFIG_PROC_FS
+static struct mount *mnt_list_next(struct mnt_namespace *ns,
+                                  struct list_head *p)
+{
+       struct mount *mnt, *ret = NULL;
+
+       lock_ns_list(ns);
+       list_for_each_continue(p, &ns->list) {
+               mnt = list_entry(p, typeof(*mnt), mnt_list);
+               if (!mnt_is_cursor(mnt)) {
+                       ret = mnt;
+                       break;
+               }
+       }
+       unlock_ns_list(ns);
+
+       return ret;
+}
+
 /* iterator; we want it to have access to namespace_sem, thus here... */
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
        struct proc_mounts *p = m->private;
+       struct list_head *prev;
 
        down_read(&namespace_sem);
-       if (p->cached_event == p->ns->event) {
-               void *v = p->cached_mount;
-               if (*pos == p->cached_index)
-                       return v;
-               if (*pos == p->cached_index + 1) {
-                       v = seq_list_next(v, &p->ns->list, &p->cached_index);
-                       return p->cached_mount = v;
-               }
+       if (!*pos) {
+               prev = &p->ns->list;
+       } else {
+               prev = &p->cursor.mnt_list;
+
+               /* Read after we'd reached the end? */
+               if (list_empty(prev))
+                       return NULL;
        }
 
-       p->cached_event = p->ns->event;
-       p->cached_mount = seq_list_start(&p->ns->list, *pos);
-       p->cached_index = *pos;
-       return p->cached_mount;
+       return mnt_list_next(p->ns, prev);
 }
 
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct proc_mounts *p = m->private;
+       struct mount *mnt = v;
 
-       p->cached_mount = seq_list_next(v, &p->ns->list, pos);
-       p->cached_index = *pos;
-       return p->cached_mount;
+       ++*pos;
+       return mnt_list_next(p->ns, &mnt->mnt_list);
 }
 
 static void m_stop(struct seq_file *m, void *v)
 {
+       struct proc_mounts *p = m->private;
+       struct mount *mnt = v;
+
+       lock_ns_list(p->ns);
+       if (mnt)
+               list_move_tail(&p->cursor.mnt_list, &mnt->mnt_list);
+       else
+               list_del_init(&p->cursor.mnt_list);
+       unlock_ns_list(p->ns);
        up_read(&namespace_sem);
 }
 
 static int m_show(struct seq_file *m, void *v)
 {
        struct proc_mounts *p = m->private;
-       struct mount *r = list_entry(v, struct mount, mnt_list);
+       struct mount *r = v;
        return p->show(m, &r->mnt);
 }
 
@@ -1294,6 +1338,15 @@ const struct seq_operations mounts_op = {
        .stop   = m_stop,
        .show   = m_show,
 };
+
+void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor)
+{
+       down_read(&namespace_sem);
+       lock_ns_list(ns);
+       list_del(&cursor->mnt_list);
+       unlock_ns_list(ns);
+       up_read(&namespace_sem);
+}
 #endif  /* CONFIG_PROC_FS */
 
 /**
@@ -3202,6 +3255,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool a
        atomic_set(&new_ns->count, 1);
        INIT_LIST_HEAD(&new_ns->list);
        init_waitqueue_head(&new_ns->poll);
+       spin_lock_init(&new_ns->ns_lock);
        new_ns->user_ns = get_user_ns(user_ns);
        new_ns->ucounts = ucounts;
        return new_ns;
@@ -3595,7 +3649,7 @@ EXPORT_SYMBOL(path_is_under);
  * file system may be mounted on put_old. After all, new_root is a mountpoint.
  *
  * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
- * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives
+ * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
  * in this situation.
  *
  * Notes:
@@ -3842,10 +3896,14 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
        bool visible = false;
 
        down_read(&namespace_sem);
+       lock_ns_list(ns);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
                struct mount *child;
                int mnt_flags;
 
+               if (mnt_is_cursor(mnt))
+                       continue;
+
                if (mnt->mnt.mnt_sb->s_type != sb->s_type)
                        continue;
 
@@ -3893,6 +3951,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
        next:   ;
        }
 found:
+       unlock_ns_list(ns);
        up_read(&namespace_sem);
        return visible;
 }
index 7a57ff2528af9da516cbb9168513e8941c318ec9..8f7cff7a42938e293072d5bc6fc4a7adc2de2c4e 100644 (file)
@@ -582,7 +582,7 @@ retry:
                if (!arg->layoutupdate_pages)
                        return -ENOMEM;
 
-               start_p = __vmalloc(buffer_size, GFP_NOFS, PAGE_KERNEL);
+               start_p = __vmalloc(buffer_size, GFP_NOFS);
                if (!start_p) {
                        kfree(arg->layoutupdate_pages);
                        return -ENOMEM;
index 1f32a9fbfdafbb21c57db8d60097bc1593837499..6673a77884d9daeeee1f11f565ef541779e28fa7 100644 (file)
@@ -668,7 +668,8 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
 }
 
 /*
- * Record the page as unstable and mark its inode as dirty.
+ * Record the page as unstable (an extra writeback period) and mark its
+ * inode as dirty.
  */
 static inline
 void nfs_mark_page_unstable(struct page *page, struct nfs_commit_info *cinfo)
@@ -676,8 +677,11 @@ void nfs_mark_page_unstable(struct page *page, struct nfs_commit_info *cinfo)
        if (!cinfo->dreq) {
                struct inode *inode = page_file_mapping(page)->host;
 
-               inc_node_page_state(page, NR_UNSTABLE_NFS);
-               inc_wb_stat(&inode_to_bdi(inode)->wb, WB_RECLAIMABLE);
+               /* This page is really still in write-back - just that the
+                * writeback is happening on the server now.
+                */
+               inc_node_page_state(page, NR_WRITEBACK);
+               inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
                __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
        }
 }
index 1e767f779c498c3b57db16d904b4fa25bdb183f1..639c34fec04a8488dd3a5df5b1b1c9ba0f170753 100644 (file)
@@ -946,9 +946,9 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
 static void
 nfs_clear_page_commit(struct page *page)
 {
-       dec_node_page_state(page, NR_UNSTABLE_NFS);
+       dec_node_page_state(page, NR_WRITEBACK);
        dec_wb_stat(&inode_to_bdi(page_file_mapping(page)->host)->wb,
-                   WB_RECLAIMABLE);
+                   WB_WRITEBACK);
 }
 
 /* Called holding the request lock on @req */
index a8fb18609146a2a07cd63d652e98770431ba76e6..9e40dfecf1b1a6dd93c322452893c5e2ccf6af51 100644 (file)
@@ -127,16 +127,8 @@ nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname)
                goto out;
        }
 
-       {
-               SHASH_DESC_ON_STACK(desc, tfm);
-
-               desc->tfm = tfm;
-
-               status = crypto_shash_digest(desc, clname->data, clname->len,
-                                            cksum.data);
-               shash_desc_zero(desc);
-       }
-
+       status = crypto_shash_tfm_digest(tfm, clname->data, clname->len,
+                                        cksum.data);
        if (status)
                goto out;
 
@@ -1148,7 +1140,6 @@ nfsd4_cld_create_v2(struct nfs4_client *clp)
        struct crypto_shash *tfm = cn->cn_tfm;
        struct xdr_netobj cksum;
        char *principal = NULL;
-       SHASH_DESC_ON_STACK(desc, tfm);
 
        /* Don't upcall if it's already stored */
        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
@@ -1170,16 +1161,14 @@ nfsd4_cld_create_v2(struct nfs4_client *clp)
        else if (clp->cl_cred.cr_principal)
                principal = clp->cl_cred.cr_principal;
        if (principal) {
-               desc->tfm = tfm;
                cksum.len = crypto_shash_digestsize(tfm);
                cksum.data = kmalloc(cksum.len, GFP_KERNEL);
                if (cksum.data == NULL) {
                        ret = -ENOMEM;
                        goto out;
                }
-               ret = crypto_shash_digest(desc, principal, strlen(principal),
-                                         cksum.data);
-               shash_desc_zero(desc);
+               ret = crypto_shash_tfm_digest(tfm, principal, strlen(principal),
+                                             cksum.data);
                if (ret) {
                        kfree(cksum.data);
                        goto out;
@@ -1343,7 +1332,6 @@ nfsd4_cld_check_v2(struct nfs4_client *clp)
        struct crypto_shash *tfm = cn->cn_tfm;
        struct xdr_netobj cksum;
        char *principal = NULL;
-       SHASH_DESC_ON_STACK(desc, tfm);
 
        /* did we already find that this client is stable? */
        if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
@@ -1381,14 +1369,12 @@ found:
                        principal = clp->cl_cred.cr_principal;
                if (principal == NULL)
                        return -ENOENT;
-               desc->tfm = tfm;
                cksum.len = crypto_shash_digestsize(tfm);
                cksum.data = kmalloc(cksum.len, GFP_KERNEL);
                if (cksum.data == NULL)
                        return -ENOENT;
-               status = crypto_shash_digest(desc, principal, strlen(principal),
-                                            cksum.data);
-               shash_desc_zero(desc);
+               status = crypto_shash_tfm_digest(tfm, principal,
+                                                strlen(principal), cksum.data);
                if (status) {
                        kfree(cksum.data);
                        return -ENOENT;
index 0aa02eb18bd3c5ca22301ac1c1ab4fd029878d0f..c3fbab1753ec8edd2fb0c593dc1aeaf6a23b6c61 100644 (file)
@@ -979,12 +979,13 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
 
        if (test_bit(RQ_LOCAL, &rqstp->rq_flags))
                /*
-                * We want less throttling in balance_dirty_pages()
-                * and shrink_inactive_list() so that nfs to
+                * We want throttling in balance_dirty_pages()
+                * and shrink_inactive_list() to only consider
+                * the backingdev we are writing to, so that nfs to
                 * localhost doesn't cause nfsd to lock up due to all
                 * the client's dirty pages or its congested queue.
                 */
-               current->flags |= PF_LESS_THROTTLE;
+               current->flags |= PF_LOCAL_THROTTLE;
 
        exp = fhp->fh_export;
        use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp);
@@ -1037,7 +1038,7 @@ out_nfserr:
                nfserr = nfserrno(host_err);
        }
        if (test_bit(RQ_LOCAL, &rqstp->rq_flags))
-               current_restore_flags(pflags, PF_LESS_THROTTLE);
+               current_restore_flags(pflags, PF_LOCAL_THROTTLE);
        return nfserr;
 }
 
index 671085512e0fde9e8be3274629db21e8c0561b96..ceeb3b4418445b85adbe39b5d92fdd99dfe9f1ca 100644 (file)
@@ -145,18 +145,9 @@ static int nilfs_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, nilfs_get_block);
 }
 
-/**
- * nilfs_readpages() - implement readpages() method of nilfs_aops {}
- * address_space_operations.
- * @file - file struct of the file to be read
- * @mapping - address_space struct used for reading multiple pages
- * @pages - the pages to be read
- * @nr_pages - number of pages to be read
- */
-static int nilfs_readpages(struct file *file, struct address_space *mapping,
-                          struct list_head *pages, unsigned int nr_pages)
+static void nilfs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, nilfs_get_block);
+       mpage_readahead(rac, nilfs_get_block);
 }
 
 static int nilfs_writepages(struct address_space *mapping,
@@ -308,7 +299,7 @@ const struct address_space_operations nilfs_aops = {
        .readpage               = nilfs_readpage,
        .writepages             = nilfs_writepages,
        .set_page_dirty         = nilfs_set_page_dirty,
-       .readpages              = nilfs_readpages,
+       .readahead              = nilfs_readahead,
        .write_begin            = nilfs_write_begin,
        .write_end              = nilfs_write_end,
        /* .releasepage         = nilfs_releasepage, */
index 5435a40f82bec95b140451057ca8415f36f5381e..c18459cea6f41867426c8aab8dedc5e2d4bf0cab 100644 (file)
@@ -520,7 +520,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
        BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
        BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
 
-       BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);
+       BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
 
        mask = fanotify_group_event_mask(group, iter_info, mask, data,
                                         data_type);
index 6736e47d94d81a0b48cc2d60dcc27be144de0f1c..7715fadd5fff6848c18921ac1585e47ba9f3a355 100644 (file)
@@ -12,6 +12,6 @@ config INOTIFY_USER
          new features including multiple file events, one-shot support, and
          unmount notification.
 
-         For more information, see <file:Documentation/filesystems/inotify.txt>
+         For more information, see <file:Documentation/filesystems/inotify.rst>
 
          If unsure, say Y.
index de9fb5cff22696d6d5de2997e1920178a1644f1b..1667a7e590d86315a6f27b3a6a9fbd930316c34f 100644 (file)
@@ -18,7 +18,7 @@ config NTFS_FS
          the Linux 2.4 kernel series is separately available as a patch
          from the project web site.
 
-         For more information see <file:Documentation/filesystems/ntfs.txt>
+         For more information see <file:Documentation/filesystems/ntfs.rst>
          and <http://www.linux-ntfs.org/>.
 
          To compile this file system support as a module, choose M here: the
index 554b744f41bf8a6f72d145fe0733180772d2a435..bb0a43860ad26dd351901c80f81fbc31f4f23634 100644 (file)
@@ -1732,7 +1732,7 @@ void mark_ntfs_record_dirty(struct page *page, const unsigned int ofs) {
                                bh = bh->b_this_page;
                        } while (bh);
                        tail->b_this_page = head;
-                       attach_page_buffers(page, head);
+                       attach_page_private(page, head);
                } else
                        buffers_to_free = bh;
        }
index 842b0bfc3ac98765198adaba55bef1d070d6c93d..7068425735f146865d8b9f5967ccb883a4da68b2 100644 (file)
@@ -34,7 +34,7 @@ static inline void *__ntfs_malloc(unsigned long size, gfp_t gfp_mask)
                /* return (void *)__get_free_page(gfp_mask); */
        }
        if (likely((size >> PAGE_SHIFT) < totalram_pages()))
-               return __vmalloc(size, gfp_mask, PAGE_KERNEL);
+               return __vmalloc(size, gfp_mask);
        return NULL;
 }
 
index 3aac5c917afe73b02f2ca5a6f63f347ffb69f629..fbb9f1bc623dc555c0f9ced52e11fd158209c09c 100644 (file)
@@ -504,7 +504,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
                        bh = bh->b_this_page;
                } while (bh);
                tail->b_this_page = head;
-               attach_page_buffers(page, head);
+               attach_page_private(page, head);
        }
        bh = head = page_buffers(page);
        BUG_ON(!bh);
index 46bba20da6b5b2e6e2c552d92e8031338d21e8f9..1177c33df8953385ffbc55c0dbbaa84efb140dad 100644 (file)
@@ -21,7 +21,7 @@ config OCFS2_FS
          OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/
 
          For more information on OCFS2, see the file
-         <file:Documentation/filesystems/ocfs2.txt>.
+         <file:Documentation/filesystems/ocfs2.rst>.
 
 config OCFS2_FS_O2CB
        tristate "O2CB Kernelspace Clustering"
index 3a67a6518ddf24c16dc7c1ea02eff3bfc466434a..3bfb4147895a093927165510aac6ae07f83c6f81 100644 (file)
@@ -350,14 +350,11 @@ out:
  * grow out to a tree. If need be, detecting boundary extents could
  * trivially be added in a future version of ocfs2_get_block().
  */
-static int ocfs2_readpages(struct file *filp, struct address_space *mapping,
-                          struct list_head *pages, unsigned nr_pages)
+static void ocfs2_readahead(struct readahead_control *rac)
 {
-       int ret, err = -EIO;
-       struct inode *inode = mapping->host;
+       int ret;
+       struct inode *inode = rac->mapping->host;
        struct ocfs2_inode_info *oi = OCFS2_I(inode);
-       loff_t start;
-       struct page *last;
 
        /*
         * Use the nonblocking flag for the dlm code to avoid page
@@ -365,36 +362,31 @@ static int ocfs2_readpages(struct file *filp, struct address_space *mapping,
         */
        ret = ocfs2_inode_lock_full(inode, NULL, 0, OCFS2_LOCK_NONBLOCK);
        if (ret)
-               return err;
+               return;
 
-       if (down_read_trylock(&oi->ip_alloc_sem) == 0) {
-               ocfs2_inode_unlock(inode, 0);
-               return err;
-       }
+       if (down_read_trylock(&oi->ip_alloc_sem) == 0)
+               goto out_unlock;
 
        /*
         * Don't bother with inline-data. There isn't anything
         * to read-ahead in that case anyway...
         */
        if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL)
-               goto out_unlock;
+               goto out_up;
 
        /*
         * Check whether a remote node truncated this file - we just
         * drop out in that case as it's not worth handling here.
         */
-       last = lru_to_page(pages);
-       start = (loff_t)last->index << PAGE_SHIFT;
-       if (start >= i_size_read(inode))
-               goto out_unlock;
+       if (readahead_pos(rac) >= i_size_read(inode))
+               goto out_up;
 
-       err = mpage_readpages(mapping, pages, nr_pages, ocfs2_get_block);
+       mpage_readahead(rac, ocfs2_get_block);
 
-out_unlock:
+out_up:
        up_read(&oi->ip_alloc_sem);
+out_unlock:
        ocfs2_inode_unlock(inode, 0);
-
-       return err;
 }
 
 /* Note: Because we don't support holes, our allocation has
@@ -2474,7 +2466,7 @@ static ssize_t ocfs2_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 
 const struct address_space_operations ocfs2_aops = {
        .readpage               = ocfs2_readpage,
-       .readpages              = ocfs2_readpages,
+       .readahead              = ocfs2_readahead,
        .writepage              = ocfs2_writepage,
        .write_begin            = ocfs2_write_begin,
        .write_end              = ocfs2_write_end,
index 55a6512e9fde8dd5fb6ed243a77598d7c4de7b01..f105746063ed32a1cca08538889328f228043bd1 100644 (file)
@@ -2760,6 +2760,7 @@ leave:
  * Returns: 1 if dlm->spinlock was dropped/retaken, 0 if never dropped
  */
 int dlm_empty_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res)
+       __must_hold(&dlm->spinlock)
 {
        int ret;
        int lock_dropped = 0;
index 1de77f1a600b20eac9414fd2d4716396b97de101..ea868c6f980053daf90e90c3acefba4f5e18e993 100644 (file)
@@ -227,7 +227,7 @@ static ssize_t dlmfs_file_read(struct file *filp,
                               loff_t *ppos)
 {
        int bytes_left;
-       ssize_t readlen, got;
+       ssize_t got;
        char *lvb_buf;
        struct inode *inode = file_inode(filp);
 
@@ -237,36 +237,31 @@ static ssize_t dlmfs_file_read(struct file *filp,
        if (*ppos >= i_size_read(inode))
                return 0;
 
+       /* don't read past the lvb */
+       if (count > i_size_read(inode) - *ppos)
+               count = i_size_read(inode) - *ppos;
+
        if (!count)
                return 0;
 
-       if (!access_ok(buf, count))
-               return -EFAULT;
-
-       /* don't read past the lvb */
-       if ((count + *ppos) > i_size_read(inode))
-               readlen = i_size_read(inode) - *ppos;
-       else
-               readlen = count;
-
-       lvb_buf = kmalloc(readlen, GFP_NOFS);
+       lvb_buf = kmalloc(count, GFP_NOFS);
        if (!lvb_buf)
                return -ENOMEM;
 
-       got = user_dlm_read_lvb(inode, lvb_buf, readlen);
+       got = user_dlm_read_lvb(inode, lvb_buf, count);
        if (got) {
-               BUG_ON(got != readlen);
-               bytes_left = __copy_to_user(buf, lvb_buf, readlen);
-               readlen -= bytes_left;
+               BUG_ON(got != count);
+               bytes_left = copy_to_user(buf, lvb_buf, count);
+               count -= bytes_left;
        } else
-               readlen = 0;
+               count = 0;
 
        kfree(lvb_buf);
 
-       *ppos = *ppos + readlen;
+       *ppos = *ppos + count;
 
-       mlog(0, "read %zd bytes\n", readlen);
-       return readlen;
+       mlog(0, "read %zu bytes\n", count);
+       return count;
 }
 
 static ssize_t dlmfs_file_write(struct file *filp,
@@ -291,9 +286,6 @@ static ssize_t dlmfs_file_write(struct file *filp,
        if (!count)
                return 0;
 
-       if (!access_ok(buf, count))
-               return -EFAULT;
-
        lvb_buf = kmalloc(count, GFP_NOFS);
        if (!lvb_buf)
                return -ENOMEM;
index 9150cfa4df7dc6bf4a0744a37eb7cfe9ef47801b..ee5d985162125342fcc2e025b2a921e7176d9b48 100644 (file)
@@ -279,6 +279,7 @@ enum ocfs2_mount_options
        OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT = 1 << 15,  /* Journal Async Commit */
        OCFS2_MOUNT_ERRORS_CONT = 1 << 16, /* Return EIO to the calling process on error */
        OCFS2_MOUNT_ERRORS_ROFS = 1 << 17, /* Change filesystem to read-only on error */
+       OCFS2_MOUNT_NOCLUSTER = 1 << 18, /* No cluster aware filesystem mount */
 };
 
 #define OCFS2_OSB_SOFT_RO      0x0001
@@ -673,7 +674,8 @@ static inline int ocfs2_cluster_o2cb_global_heartbeat(struct ocfs2_super *osb)
 
 static inline int ocfs2_mount_local(struct ocfs2_super *osb)
 {
-       return (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT);
+       return ((osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT)
+               || (osb->s_mount_opt & OCFS2_MOUNT_NOCLUSTER));
 }
 
 static inline int ocfs2_uses_extended_slot_map(struct ocfs2_super *osb)
index 8caeceeaeda7c7d8f783d379a6cbf6faf2cb9f8d..4da0e4b1e79bf0b89d99568ddbd4b5acf28a0f09 100644 (file)
@@ -254,14 +254,16 @@ static int __ocfs2_find_empty_slot(struct ocfs2_slot_info *si,
        int i, ret = -ENOSPC;
 
        if ((preferred >= 0) && (preferred < si->si_num_slots)) {
-               if (!si->si_slots[preferred].sl_valid) {
+               if (!si->si_slots[preferred].sl_valid ||
+                   !si->si_slots[preferred].sl_node_num) {
                        ret = preferred;
                        goto out;
                }
        }
 
        for(i = 0; i < si->si_num_slots; i++) {
-               if (!si->si_slots[i].sl_valid) {
+               if (!si->si_slots[i].sl_valid ||
+                   !si->si_slots[i].sl_node_num) {
                        ret = i;
                        break;
                }
@@ -456,24 +458,30 @@ int ocfs2_find_slot(struct ocfs2_super *osb)
        spin_lock(&osb->osb_lock);
        ocfs2_update_slot_info(si);
 
-       /* search for ourselves first and take the slot if it already
-        * exists. Perhaps we need to mark this in a variable for our
-        * own journal recovery? Possibly not, though we certainly
-        * need to warn to the user */
-       slot = __ocfs2_node_num_to_slot(si, osb->node_num);
-       if (slot < 0) {
-               /* if no slot yet, then just take 1st available
-                * one. */
-               slot = __ocfs2_find_empty_slot(si, osb->preferred_slot);
+       if (ocfs2_mount_local(osb))
+               /* use slot 0 directly in local mode */
+               slot = 0;
+       else {
+               /* search for ourselves first and take the slot if it already
+                * exists. Perhaps we need to mark this in a variable for our
+                * own journal recovery? Possibly not, though we certainly
+                * need to warn to the user */
+               slot = __ocfs2_node_num_to_slot(si, osb->node_num);
                if (slot < 0) {
-                       spin_unlock(&osb->osb_lock);
-                       mlog(ML_ERROR, "no free slots available!\n");
-                       status = -EINVAL;
-                       goto bail;
-               }
-       } else
-               printk(KERN_INFO "ocfs2: Slot %d on device (%s) was already "
-                      "allocated to this node!\n", slot, osb->dev_str);
+                       /* if no slot yet, then just take 1st available
+                        * one. */
+                       slot = __ocfs2_find_empty_slot(si, osb->preferred_slot);
+                       if (slot < 0) {
+                               spin_unlock(&osb->osb_lock);
+                               mlog(ML_ERROR, "no free slots available!\n");
+                               status = -EINVAL;
+                               goto bail;
+                       }
+               } else
+                       printk(KERN_INFO "ocfs2: Slot %d on device (%s) was "
+                              "already allocated to this node!\n",
+                              slot, osb->dev_str);
+       }
 
        ocfs2_set_slot(si, slot, osb->node_num);
        osb->slot_num = slot;
index ac61eeaf38374aa31ee739e2604f92a9832c40d5..71ea9ce71a6b8a88039ebec5d2b25145c1273197 100644 (file)
@@ -175,6 +175,7 @@ enum {
        Opt_dir_resv_level,
        Opt_journal_async_commit,
        Opt_err_cont,
+       Opt_nocluster,
        Opt_err,
 };
 
@@ -208,6 +209,7 @@ static const match_table_t tokens = {
        {Opt_dir_resv_level, "dir_resv_level=%u"},
        {Opt_journal_async_commit, "journal_async_commit"},
        {Opt_err_cont, "errors=continue"},
+       {Opt_nocluster, "nocluster"},
        {Opt_err, NULL}
 };
 
@@ -619,6 +621,13 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
                goto out;
        }
 
+       tmp = OCFS2_MOUNT_NOCLUSTER;
+       if ((osb->s_mount_opt & tmp) != (parsed_options.mount_opt & tmp)) {
+               ret = -EINVAL;
+               mlog(ML_ERROR, "Cannot change nocluster option on remount\n");
+               goto out;
+       }
+
        tmp = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL |
                OCFS2_MOUNT_HB_NONE;
        if ((osb->s_mount_opt & tmp) != (parsed_options.mount_opt & tmp)) {
@@ -859,6 +868,7 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb,
        }
 
        if (ocfs2_userspace_stack(osb) &&
+           !(osb->s_mount_opt & OCFS2_MOUNT_NOCLUSTER) &&
            strncmp(osb->osb_cluster_stack, mopt->cluster_stack,
                    OCFS2_STACK_LABEL_LEN)) {
                mlog(ML_ERROR,
@@ -1139,6 +1149,11 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
               osb->s_mount_opt & OCFS2_MOUNT_DATA_WRITEBACK ? "writeback" :
               "ordered");
 
+       if ((osb->s_mount_opt & OCFS2_MOUNT_NOCLUSTER) &&
+          !(osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT))
+               printk(KERN_NOTICE "ocfs2: The shared device (%s) is mounted "
+                      "without cluster aware mode.\n", osb->dev_str);
+
        atomic_set(&osb->vol_state, VOLUME_MOUNTED);
        wake_up(&osb->osb_mount_event);
 
@@ -1445,6 +1460,9 @@ static int ocfs2_parse_options(struct super_block *sb,
                case Opt_journal_async_commit:
                        mopt->mount_opt |= OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT;
                        break;
+               case Opt_nocluster:
+                       mopt->mount_opt |= OCFS2_MOUNT_NOCLUSTER;
+                       break;
                default:
                        mlog(ML_ERROR,
                             "Unrecognized mount option \"%s\" "
@@ -1556,6 +1574,9 @@ static int ocfs2_show_options(struct seq_file *s, struct dentry *root)
        if (opts & OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT)
                seq_printf(s, ",journal_async_commit");
 
+       if (opts & OCFS2_MOUNT_NOCLUSTER)
+               seq_printf(s, ",nocluster");
+
        return 0;
 }
 
index d640b9388238592762bd890268f01a549c521b2e..d7b5f09d298c9d7dbfbc68fa1bce106c1cdde051 100644 (file)
@@ -289,10 +289,9 @@ static int omfs_readpage(struct file *file, struct page *page)
        return block_read_full_page(page, omfs_get_block);
 }
 
-static int omfs_readpages(struct file *file, struct address_space *mapping,
-               struct list_head *pages, unsigned nr_pages)
+static void omfs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, omfs_get_block);
+       mpage_readahead(rac, omfs_get_block);
 }
 
 static int omfs_writepage(struct page *page, struct writeback_control *wbc)
@@ -373,7 +372,7 @@ const struct inode_operations omfs_file_inops = {
 
 const struct address_space_operations omfs_aops = {
        .readpage = omfs_readpage,
-       .readpages = omfs_readpages,
+       .readahead = omfs_readahead,
        .writepage = omfs_writepage,
        .writepages = omfs_writepages,
        .write_begin = omfs_write_begin,
index 719b320ede52bb1482afd28fc65b8282e9d6fb94..6cd48a61cda3b969c0717c30c8bed879efa3d8cc 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -345,21 +345,14 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
  * We do this by temporarily clearing all FS-related capabilities and
  * switching the fsuid/fsgid around to the real ones.
  */
-long do_faccessat(int dfd, const char __user *filename, int mode)
+static const struct cred *access_override_creds(void)
 {
        const struct cred *old_cred;
        struct cred *override_cred;
-       struct path path;
-       struct inode *inode;
-       int res;
-       unsigned int lookup_flags = LOOKUP_FOLLOW;
-
-       if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
-               return -EINVAL;
 
        override_cred = prepare_creds();
        if (!override_cred)
-               return -ENOMEM;
+               return NULL;
 
        override_cred->fsuid = override_cred->uid;
        override_cred->fsgid = override_cred->gid;
@@ -394,6 +387,38 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
        override_cred->non_rcu = 1;
 
        old_cred = override_creds(override_cred);
+
+       /* override_cred() gets its own ref */
+       put_cred(override_cred);
+
+       return old_cred;
+}
+
+long do_faccessat(int dfd, const char __user *filename, int mode, int flags)
+{
+       struct path path;
+       struct inode *inode;
+       int res;
+       unsigned int lookup_flags = LOOKUP_FOLLOW;
+       const struct cred *old_cred = NULL;
+
+       if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
+               return -EINVAL;
+
+       if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
+               return -EINVAL;
+
+       if (flags & AT_SYMLINK_NOFOLLOW)
+               lookup_flags &= ~LOOKUP_FOLLOW;
+       if (flags & AT_EMPTY_PATH)
+               lookup_flags |= LOOKUP_EMPTY;
+
+       if (!(flags & AT_EACCESS)) {
+               old_cred = access_override_creds();
+               if (!old_cred)
+                       return -ENOMEM;
+       }
+
 retry:
        res = user_path_at(dfd, filename, lookup_flags, &path);
        if (res)
@@ -435,19 +460,26 @@ out_path_release:
                goto retry;
        }
 out:
-       revert_creds(old_cred);
-       put_cred(override_cred);
+       if (old_cred)
+               revert_creds(old_cred);
+
        return res;
 }
 
 SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 {
-       return do_faccessat(dfd, filename, mode);
+       return do_faccessat(dfd, filename, mode, 0);
+}
+
+SYSCALL_DEFINE4(faccessat2, int, dfd, const char __user *, filename, int, mode,
+               int, flags)
+{
+       return do_faccessat(dfd, filename, mode, flags);
 }
 
 SYSCALL_DEFINE2(access, const char __user *, filename, int, mode)
 {
-       return do_faccessat(AT_FDCWD, filename, mode);
+       return do_faccessat(AT_FDCWD, filename, mode, 0);
 }
 
 int ksys_chdir(const char __user *filename)
@@ -743,9 +775,8 @@ static int do_dentry_open(struct file *f,
        path_get(&f->f_path);
        f->f_inode = inode;
        f->f_mapping = inode->i_mapping;
-
-       /* Ensure that we skip any errors that predate opening of the file */
        f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
+       f->f_sb_err = file_sample_sb_err(f);
 
        if (unlikely(f->f_flags & O_PATH)) {
                f->f_mode = FMODE_PATH | FMODE_OPENED;
index 12ae630fbed7a013751ae1c0e2577ac4896b0861..48f0547d4850e985411e477dcc61b08721b3f516 100644 (file)
@@ -62,12 +62,7 @@ static int orangefs_writepage_locked(struct page *page,
        } else {
                ret = 0;
        }
-       if (wr) {
-               kfree(wr);
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
-               put_page(page);
-       }
+       kfree(detach_page_private(page));
        return ret;
 }
 
@@ -409,9 +404,7 @@ static int orangefs_write_begin(struct file *file,
        wr->len = len;
        wr->uid = current_fsuid();
        wr->gid = current_fsgid();
-       SetPagePrivate(page);
-       set_page_private(page, (unsigned long)wr);
-       get_page(page);
+       attach_page_private(page, wr);
 okay:
        return 0;
 }
@@ -459,18 +452,12 @@ static void orangefs_invalidatepage(struct page *page,
        wr = (struct orangefs_write_range *)page_private(page);
 
        if (offset == 0 && length == PAGE_SIZE) {
-               kfree((struct orangefs_write_range *)page_private(page));
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
-               put_page(page);
+               kfree(detach_page_private(page));
                return;
        /* write range entirely within invalidate range (or equal) */
        } else if (page_offset(page) + offset <= wr->pos &&
            wr->pos + wr->len <= page_offset(page) + offset + length) {
-               kfree((struct orangefs_write_range *)page_private(page));
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
-               put_page(page);
+               kfree(detach_page_private(page));
                /* XXX is this right? only caller in fs */
                cancel_dirty_page(page);
                return;
@@ -535,12 +522,7 @@ static int orangefs_releasepage(struct page *page, gfp_t foo)
 
 static void orangefs_freepage(struct page *page)
 {
-       if (PagePrivate(page)) {
-               kfree((struct orangefs_write_range *)page_private(page));
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
-               put_page(page);
-       }
+       kfree(detach_page_private(page));
 }
 
 static int orangefs_launder_page(struct page *page)
@@ -740,9 +722,7 @@ vm_fault_t orangefs_page_mkwrite(struct vm_fault *vmf)
        wr->len = PAGE_SIZE;
        wr->uid = current_fsuid();
        wr->gid = current_fsgid();
-       SetPagePrivate(page);
-       set_page_private(page, (unsigned long)wr);
-       get_page(page);
+       attach_page_private(page, wr);
 okay:
 
        file_update_time(vmf->vma->vm_file);
index 714c14c47ca5573053b43fb605c69761d2157562..dd188c7996b31818c437d57d5b9a1037b52fd2ed 100644 (file)
@@ -9,7 +9,7 @@ config OVERLAY_FS
          'lower' filesystem is either hidden or, in the case of directories,
          merged with the 'upper' object.
 
-         For more information see Documentation/filesystems/overlayfs.txt
+         For more information see Documentation/filesystems/overlayfs.rst
 
 config OVERLAY_FS_REDIRECT_DIR
        bool "Overlayfs: turn on redirect directory feature by default"
@@ -38,7 +38,7 @@ config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW
          If backward compatibility is not an issue, then it is safe and
          recommended to say N here.
 
-         For more information, see Documentation/filesystems/overlayfs.txt
+         For more information, see Documentation/filesystems/overlayfs.rst
 
          If unsure, say Y.
 
@@ -103,7 +103,7 @@ config OVERLAY_FS_XINO_AUTO
          If compatibility with applications that expect 32bit inodes is not an
          issue, then it is safe and recommended to say Y here.
 
-         For more information, see Documentation/filesystems/overlayfs.txt
+         For more information, see Documentation/filesystems/overlayfs.rst
 
          If unsure, say N.
 
index 27ef84d99f59c007e24246fa92ad3917ad674422..971a42f6357d0e8f4ac7c2f556200f91a6a7624a 100644 (file)
@@ -23,7 +23,7 @@ config PROC_FS
          /proc" or the equivalent line in /etc/fstab does the job.
 
          The /proc file system is explained in the file
-         <file:Documentation/filesystems/proc.txt> and on the proc(5) manpage
+         <file:Documentation/filesystems/proc.rst> and on the proc(5) manpage
          ("man 5 proc").
 
          This option will enlarge your kernel by about 67 KB. Several
@@ -95,7 +95,7 @@ config PROC_CHILDREN
        default n
        help
          Provides a fast way to retrieve first level children pids of a task. See
-         <file:Documentation/filesystems/proc.txt> for more information.
+         <file:Documentation/filesystems/proc.rst> for more information.
 
          Say Y if you are running any user-space software which takes benefit from
          this interface. For example, rkt is such a piece of software.
index 8c1f1bb1a5ce3fd1d9e757a87805fb3df638206c..ecc63ce01be7b51c68975b1f681f002aaf1a26f5 100644 (file)
@@ -103,11 +103,14 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
        show_val_kb(m, "SUnreclaim:     ", sunreclaim);
        seq_printf(m, "KernelStack:    %8lu kB\n",
                   global_zone_page_state(NR_KERNEL_STACK_KB));
+#ifdef CONFIG_SHADOW_CALL_STACK
+       seq_printf(m, "ShadowCallStack:%8lu kB\n",
+                  global_zone_page_state(NR_KERNEL_SCS_KB));
+#endif
        show_val_kb(m, "PageTables:     ",
                    global_zone_page_state(NR_PAGETABLE));
 
-       show_val_kb(m, "NFS_Unstable:   ",
-                   global_node_page_state(NR_UNSTABLE_NFS));
+       show_val_kb(m, "NFS_Unstable:   ", 0);
        show_val_kb(m, "Bounce:         ",
                    global_zone_page_state(NR_BOUNCE));
        show_val_kb(m, "WritebackTmp:   ",
index 8d382d4ec0672f32549ac9b2dd9d156fb16dd1da..6ad407d5efe2ff4bc8f854a682824232f74a007d 100644 (file)
@@ -546,10 +546,17 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
        struct mem_size_stats *mss = walk->private;
        struct vm_area_struct *vma = walk->vma;
        bool locked = !!(vma->vm_flags & VM_LOCKED);
-       struct page *page;
+       struct page *page = NULL;
 
-       /* FOLL_DUMP will return -EFAULT on huge zero page */
-       page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP);
+       if (pmd_present(*pmd)) {
+               /* FOLL_DUMP will return -EFAULT on huge zero page */
+               page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP);
+       } else if (unlikely(thp_migration_supported() && is_swap_pmd(*pmd))) {
+               swp_entry_t entry = pmd_to_swp_entry(*pmd);
+
+               if (is_migration_entry(entry))
+                       page = migration_entry_to_page(entry);
+       }
        if (IS_ERR_OR_NULL(page))
                return;
        if (PageAnon(page))
@@ -578,8 +585,7 @@ static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 
        ptl = pmd_trans_huge_lock(pmd, vma);
        if (ptl) {
-               if (pmd_present(*pmd))
-                       smaps_pmd_entry(pmd, addr, walk);
+               smaps_pmd_entry(pmd, addr, walk);
                spin_unlock(ptl);
                goto out;
        }
@@ -622,9 +628,6 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
                [ilog2(VM_GROWSDOWN)]   = "gd",
                [ilog2(VM_PFNMAP)]      = "pf",
                [ilog2(VM_DENYWRITE)]   = "dw",
-#ifdef CONFIG_X86_INTEL_MPX
-               [ilog2(VM_MPX)]         = "mp",
-#endif
                [ilog2(VM_LOCKED)]      = "lo",
                [ilog2(VM_IO)]          = "io",
                [ilog2(VM_SEQ_READ)]    = "sr",
@@ -638,6 +641,9 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma)
                [ilog2(VM_ARCH_1)]      = "ar",
                [ilog2(VM_WIPEONFORK)]  = "wf",
                [ilog2(VM_DONTDUMP)]    = "dd",
+#ifdef CONFIG_ARM64_BTI
+               [ilog2(VM_ARM64_BTI)]   = "bt",
+#endif
 #ifdef CONFIG_MEM_SOFT_DIRTY
                [ilog2(VM_SOFTDIRTY)]   = "sd",
 #endif
index 273ee82d8aa97d8f83bfbfc986f1e8b1c65d640a..e4d70c0dffe9bb380a21a03af526ec9890be796d 100644 (file)
@@ -279,7 +279,8 @@ static int mounts_open_common(struct inode *inode, struct file *file,
        p->ns = ns;
        p->root = root;
        p->show = show;
-       p->cached_event = ~0ULL;
+       INIT_LIST_HEAD(&p->cursor.mnt_list);
+       p->cursor.mnt.mnt_flags = MNT_CURSOR;
 
        return 0;
 
@@ -296,6 +297,7 @@ static int mounts_release(struct inode *inode, struct file *file)
        struct seq_file *m = file->private_data;
        struct proc_mounts *p = m->private;
        path_put(&p->root);
+       mnt_cursor_del(p->ns, &p->cursor);
        put_mnt_ns(p->ns);
        return seq_release_private(inode, file);
 }
index 8f0369aad22afc1a8172dae45d410cce7ee32dc1..e16a49ebfe546dbecf0e64c8e5838a180161ebb2 100644 (file)
@@ -153,3 +153,112 @@ config PSTORE_RAM
          "ramoops.ko".
 
          For more information, see Documentation/admin-guide/ramoops.rst.
+
+config PSTORE_ZONE
+       tristate
+       depends on PSTORE
+       help
+         The common layer for pstore/blk (and pstore/ram in the future)
+         to manage storage in zones.
+
+config PSTORE_BLK
+       tristate "Log panic/oops to a block device"
+       depends on PSTORE
+       depends on BLOCK
+       select PSTORE_ZONE
+       default n
+       help
+         This enables panic and oops message to be logged to a block dev
+         where it can be read back at some later point.
+
+         For more information, see Documentation/admin-guide/pstore-blk.rst
+
+         If unsure, say N.
+
+config PSTORE_BLK_BLKDEV
+       string "block device identifier"
+       depends on PSTORE_BLK
+       default ""
+       help
+         Which block device should be used for pstore/blk.
+
+         It accepts the following variants:
+         1) <hex_major><hex_minor> device number in hexadecimal representation,
+            with no leading 0x, for example b302.
+         2) /dev/<disk_name> represents the device name of disk
+         3) /dev/<disk_name><decimal> represents the device name and number
+            of partition - device number of disk plus the partition number
+         4) /dev/<disk_name>p<decimal> - same as the above, this form is
+            used when disk name of partitioned disk ends with a digit.
+         5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+            unique id of a partition if the partition table provides it.
+            The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+            partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+            filled hex representation of the 32-bit "NT disk signature", and PP
+            is a zero-filled hex representation of the 1-based partition number.
+         6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation
+            to a partition with a known unique id.
+         7) <major>:<minor> major and minor number of the device separated by
+            a colon.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_KMSG_SIZE
+       int "Size in Kbytes of kmsg dump log to store"
+       depends on PSTORE_BLK
+       default 64
+       help
+         This just sets size of kmsg dump (oops, panic, etc) log for
+         pstore/blk. The size is in KB and must be a multiple of 4.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_MAX_REASON
+       int "Maximum kmsg dump reason to store"
+       depends on PSTORE_BLK
+       default 2
+       help
+         The maximum reason for kmsg dumps to store. The default is
+         2 (KMSG_DUMP_OOPS), see include/linux/kmsg_dump.h's
+         enum kmsg_dump_reason for more details.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_PMSG_SIZE
+       int "Size in Kbytes of pmsg to store"
+       depends on PSTORE_BLK
+       depends on PSTORE_PMSG
+       default 64
+       help
+         This just sets size of pmsg (pmsg_size) for pstore/blk. The size is
+         in KB and must be a multiple of 4.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_CONSOLE_SIZE
+       int "Size in Kbytes of console log to store"
+       depends on PSTORE_BLK
+       depends on PSTORE_CONSOLE
+       default 64
+       help
+         This just sets size of console log (console_size) to store via
+         pstore/blk. The size is in KB and must be a multiple of 4.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
+
+config PSTORE_BLK_FTRACE_SIZE
+       int "Size in Kbytes of ftrace log to store"
+       depends on PSTORE_BLK
+       depends on PSTORE_FTRACE
+       default 64
+       help
+         This just sets size of ftrace log (ftrace_size) for pstore/blk. The
+         size is in KB and must be a multiple of 4.
+
+         NOTE that, both Kconfig and module parameters can configure
+         pstore/blk, but module parameters have priority over Kconfig.
index 967b5891f325d487dcd24b7249dd3b28e60bc89c..c270467aeecea76458ba54a7fc23975ff18786c6 100644 (file)
@@ -12,3 +12,9 @@ pstore-$(CONFIG_PSTORE_PMSG)  += pmsg.o
 
 ramoops-objs += ram.o ram_core.o
 obj-$(CONFIG_PSTORE_RAM)       += ramoops.o
+
+pstore_zone-objs += zone.o
+obj-$(CONFIG_PSTORE_ZONE)      += pstore_zone.o
+
+pstore_blk-objs += blk.o
+obj-$(CONFIG_PSTORE_BLK)       += pstore_blk.o
diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c
new file mode 100644 (file)
index 0000000..fcd5563
--- /dev/null
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Implements pstore backend driver that write to block (or non-block) storage
+ * devices, using the pstore/zone API.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "../../block/blk.h"
+#include <linux/blkdev.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pstore_blk.h>
+#include <linux/mount.h>
+#include <linux/uio.h>
+
+static long kmsg_size = CONFIG_PSTORE_BLK_KMSG_SIZE;
+module_param(kmsg_size, long, 0400);
+MODULE_PARM_DESC(kmsg_size, "kmsg dump record size in kbytes");
+
+static int max_reason = CONFIG_PSTORE_BLK_MAX_REASON;
+module_param(max_reason, int, 0400);
+MODULE_PARM_DESC(max_reason,
+                "maximum reason for kmsg dump (default 2: Oops and Panic)");
+
+#if IS_ENABLED(CONFIG_PSTORE_PMSG)
+static long pmsg_size = CONFIG_PSTORE_BLK_PMSG_SIZE;
+#else
+static long pmsg_size = -1;
+#endif
+module_param(pmsg_size, long, 0400);
+MODULE_PARM_DESC(pmsg_size, "pmsg size in kbytes");
+
+#if IS_ENABLED(CONFIG_PSTORE_CONSOLE)
+static long console_size = CONFIG_PSTORE_BLK_CONSOLE_SIZE;
+#else
+static long console_size = -1;
+#endif
+module_param(console_size, long, 0400);
+MODULE_PARM_DESC(console_size, "console size in kbytes");
+
+#if IS_ENABLED(CONFIG_PSTORE_FTRACE)
+static long ftrace_size = CONFIG_PSTORE_BLK_FTRACE_SIZE;
+#else
+static long ftrace_size = -1;
+#endif
+module_param(ftrace_size, long, 0400);
+MODULE_PARM_DESC(ftrace_size, "ftrace size in kbytes");
+
+static bool best_effort;
+module_param(best_effort, bool, 0400);
+MODULE_PARM_DESC(best_effort, "use best effort to write (i.e. do not require storage driver pstore support, default: off)");
+
+/*
+ * blkdev - the block device to use for pstore storage
+ *
+ * Usually, this will be a partition of a block device.
+ *
+ * blkdev accepts the following variants:
+ * 1) <hex_major><hex_minor> device number in hexadecimal representation,
+ *    with no leading 0x, for example b302.
+ * 2) /dev/<disk_name> represents the device number of disk
+ * 3) /dev/<disk_name><decimal> represents the device number
+ *    of partition - device number of disk plus the partition number
+ * 4) /dev/<disk_name>p<decimal> - same as the above, that form is
+ *    used when disk name of partitioned disk ends on a digit.
+ * 5) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the
+ *    unique id of a partition if the partition table provides it.
+ *    The UUID may be either an EFI/GPT UUID, or refer to an MSDOS
+ *    partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero-
+ *    filled hex representation of the 32-bit "NT disk signature", and PP
+ *    is a zero-filled hex representation of the 1-based partition number.
+ * 6) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to
+ *    a partition with a known unique id.
+ * 7) <major>:<minor> major and minor number of the device separated by
+ *    a colon.
+ */
+static char blkdev[80] = CONFIG_PSTORE_BLK_BLKDEV;
+module_param_string(blkdev, blkdev, 80, 0400);
+MODULE_PARM_DESC(blkdev, "block device for pstore storage");
+
+/*
+ * All globals must only be accessed under the pstore_blk_lock
+ * during the register/unregister functions.
+ */
+static DEFINE_MUTEX(pstore_blk_lock);
+static struct block_device *psblk_bdev;
+static struct pstore_zone_info *pstore_zone_info;
+static pstore_blk_panic_write_op blkdev_panic_write;
+
+struct bdev_info {
+       dev_t devt;
+       sector_t nr_sects;
+       sector_t start_sect;
+};
+
+#define check_size(name, alignsize) ({                         \
+       long _##name_ = (name);                                 \
+       _##name_ = _##name_ <= 0 ? 0 : (_##name_ * 1024);       \
+       if (_##name_ & ((alignsize) - 1)) {                     \
+               pr_info(#name " must align to %d\n",            \
+                               (alignsize));                   \
+               _##name_ = ALIGN(name, (alignsize));            \
+       }                                                       \
+       _##name_;                                               \
+})
+
+static int __register_pstore_device(struct pstore_device_info *dev)
+{
+       int ret;
+
+       lockdep_assert_held(&pstore_blk_lock);
+
+       if (!dev || !dev->total_size || !dev->read || !dev->write)
+               return -EINVAL;
+
+       /* someone already registered before */
+       if (pstore_zone_info)
+               return -EBUSY;
+
+       pstore_zone_info = kzalloc(sizeof(struct pstore_zone_info), GFP_KERNEL);
+       if (!pstore_zone_info)
+               return -ENOMEM;
+
+       /* zero means not limit on which backends to attempt to store. */
+       if (!dev->flags)
+               dev->flags = UINT_MAX;
+
+#define verify_size(name, alignsize, enabled) {                                \
+               long _##name_;                                          \
+               if (enabled)                                            \
+                       _##name_ = check_size(name, alignsize);         \
+               else                                                    \
+                       _##name_ = 0;                                   \
+               name = _##name_ / 1024;                                 \
+               pstore_zone_info->name = _##name_;                      \
+       }
+
+       verify_size(kmsg_size, 4096, dev->flags & PSTORE_FLAGS_DMESG);
+       verify_size(pmsg_size, 4096, dev->flags & PSTORE_FLAGS_PMSG);
+       verify_size(console_size, 4096, dev->flags & PSTORE_FLAGS_CONSOLE);
+       verify_size(ftrace_size, 4096, dev->flags & PSTORE_FLAGS_FTRACE);
+#undef verify_size
+
+       pstore_zone_info->total_size = dev->total_size;
+       pstore_zone_info->max_reason = max_reason;
+       pstore_zone_info->read = dev->read;
+       pstore_zone_info->write = dev->write;
+       pstore_zone_info->erase = dev->erase;
+       pstore_zone_info->panic_write = dev->panic_write;
+       pstore_zone_info->name = KBUILD_MODNAME;
+       pstore_zone_info->owner = THIS_MODULE;
+
+       ret = register_pstore_zone(pstore_zone_info);
+       if (ret) {
+               kfree(pstore_zone_info);
+               pstore_zone_info = NULL;
+       }
+       return ret;
+}
+/**
+ * register_pstore_device() - register non-block device to pstore/blk
+ *
+ * @dev: non-block device information
+ *
+ * Return:
+ * * 0         - OK
+ * * Others    - something error.
+ */
+int register_pstore_device(struct pstore_device_info *dev)
+{
+       int ret;
+
+       mutex_lock(&pstore_blk_lock);
+       ret = __register_pstore_device(dev);
+       mutex_unlock(&pstore_blk_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(register_pstore_device);
+
+static void __unregister_pstore_device(struct pstore_device_info *dev)
+{
+       lockdep_assert_held(&pstore_blk_lock);
+       if (pstore_zone_info && pstore_zone_info->read == dev->read) {
+               unregister_pstore_zone(pstore_zone_info);
+               kfree(pstore_zone_info);
+               pstore_zone_info = NULL;
+       }
+}
+
+/**
+ * unregister_pstore_device() - unregister non-block device from pstore/blk
+ *
+ * @dev: non-block device information
+ */
+void unregister_pstore_device(struct pstore_device_info *dev)
+{
+       mutex_lock(&pstore_blk_lock);
+       __unregister_pstore_device(dev);
+       mutex_unlock(&pstore_blk_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_pstore_device);
+
+/**
+ * psblk_get_bdev() - open block device
+ *
+ * @holder:    Exclusive holder identifier
+ * @info:      Information about bdev to fill in
+ *
+ * Return: pointer to block device on success and others on error.
+ *
+ * On success, the returned block_device has reference count of one.
+ */
+static struct block_device *psblk_get_bdev(void *holder,
+                                          struct bdev_info *info)
+{
+       struct block_device *bdev = ERR_PTR(-ENODEV);
+       fmode_t mode = FMODE_READ | FMODE_WRITE;
+       sector_t nr_sects;
+
+       lockdep_assert_held(&pstore_blk_lock);
+
+       if (pstore_zone_info)
+               return ERR_PTR(-EBUSY);
+
+       if (!blkdev[0])
+               return ERR_PTR(-ENODEV);
+
+       if (holder)
+               mode |= FMODE_EXCL;
+       bdev = blkdev_get_by_path(blkdev, mode, holder);
+       if (IS_ERR(bdev)) {
+               dev_t devt;
+
+               devt = name_to_dev_t(blkdev);
+               if (devt == 0)
+                       return ERR_PTR(-ENODEV);
+               bdev = blkdev_get_by_dev(devt, mode, holder);
+               if (IS_ERR(bdev))
+                       return bdev;
+       }
+
+       nr_sects = part_nr_sects_read(bdev->bd_part);
+       if (!nr_sects) {
+               pr_err("not enough space for '%s'\n", blkdev);
+               blkdev_put(bdev, mode);
+               return ERR_PTR(-ENOSPC);
+       }
+
+       if (info) {
+               info->devt = bdev->bd_dev;
+               info->nr_sects = nr_sects;
+               info->start_sect = get_start_sect(bdev);
+       }
+
+       return bdev;
+}
+
+static void psblk_put_bdev(struct block_device *bdev, void *holder)
+{
+       fmode_t mode = FMODE_READ | FMODE_WRITE;
+
+       lockdep_assert_held(&pstore_blk_lock);
+
+       if (!bdev)
+               return;
+
+       if (holder)
+               mode |= FMODE_EXCL;
+       blkdev_put(bdev, mode);
+}
+
+static ssize_t psblk_generic_blk_read(char *buf, size_t bytes, loff_t pos)
+{
+       struct block_device *bdev = psblk_bdev;
+       struct file file;
+       struct kiocb kiocb;
+       struct iov_iter iter;
+       struct kvec iov = {.iov_base = buf, .iov_len = bytes};
+
+       if (!bdev)
+               return -ENODEV;
+
+       memset(&file, 0, sizeof(struct file));
+       file.f_mapping = bdev->bd_inode->i_mapping;
+       file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+       file.f_inode = bdev->bd_inode;
+       file_ra_state_init(&file.f_ra, file.f_mapping);
+
+       init_sync_kiocb(&kiocb, &file);
+       kiocb.ki_pos = pos;
+       iov_iter_kvec(&iter, READ, &iov, 1, bytes);
+
+       return generic_file_read_iter(&kiocb, &iter);
+}
+
+static ssize_t psblk_generic_blk_write(const char *buf, size_t bytes,
+               loff_t pos)
+{
+       struct block_device *bdev = psblk_bdev;
+       struct iov_iter iter;
+       struct kiocb kiocb;
+       struct file file;
+       ssize_t ret;
+       struct kvec iov = {.iov_base = (void *)buf, .iov_len = bytes};
+
+       if (!bdev)
+               return -ENODEV;
+
+       /* Console/Ftrace backend may handle buffer until flush dirty zones */
+       if (in_interrupt() || irqs_disabled())
+               return -EBUSY;
+
+       memset(&file, 0, sizeof(struct file));
+       file.f_mapping = bdev->bd_inode->i_mapping;
+       file.f_flags = O_DSYNC | __O_SYNC | O_NOATIME;
+       file.f_inode = bdev->bd_inode;
+
+       init_sync_kiocb(&kiocb, &file);
+       kiocb.ki_pos = pos;
+       iov_iter_kvec(&iter, WRITE, &iov, 1, bytes);
+
+       inode_lock(bdev->bd_inode);
+       ret = generic_write_checks(&kiocb, &iter);
+       if (ret > 0)
+               ret = generic_perform_write(&file, &iter, pos);
+       inode_unlock(bdev->bd_inode);
+
+       if (likely(ret > 0)) {
+               const struct file_operations f_op = {.fsync = blkdev_fsync};
+
+               file.f_op = &f_op;
+               kiocb.ki_pos += ret;
+               ret = generic_write_sync(&kiocb, ret);
+       }
+       return ret;
+}
+
+static ssize_t psblk_blk_panic_write(const char *buf, size_t size,
+               loff_t off)
+{
+       int ret;
+
+       if (!blkdev_panic_write)
+               return -EOPNOTSUPP;
+
+       /* size and off must align to SECTOR_SIZE for block device */
+       ret = blkdev_panic_write(buf, off >> SECTOR_SHIFT,
+                       size >> SECTOR_SHIFT);
+       /* try next zone */
+       if (ret == -ENOMSG)
+               return ret;
+       return ret ? -EIO : size;
+}
+
+static int __register_pstore_blk(struct pstore_blk_info *info)
+{
+       char bdev_name[BDEVNAME_SIZE];
+       struct block_device *bdev;
+       struct pstore_device_info dev;
+       struct bdev_info binfo;
+       void *holder = blkdev;
+       int ret = -ENODEV;
+
+       lockdep_assert_held(&pstore_blk_lock);
+
+       /* hold bdev exclusively */
+       memset(&binfo, 0, sizeof(binfo));
+       bdev = psblk_get_bdev(holder, &binfo);
+       if (IS_ERR(bdev)) {
+               pr_err("failed to open '%s'!\n", blkdev);
+               return PTR_ERR(bdev);
+       }
+
+       /* only allow driver matching the @blkdev */
+       if (!binfo.devt || (!best_effort &&
+                           MAJOR(binfo.devt) != info->major)) {
+               pr_debug("invalid major %u (expect %u)\n",
+                               info->major, MAJOR(binfo.devt));
+               ret = -ENODEV;
+               goto err_put_bdev;
+       }
+
+       /* psblk_bdev must be assigned before register to pstore/blk */
+       psblk_bdev = bdev;
+       blkdev_panic_write = info->panic_write;
+
+       /* Copy back block device details. */
+       info->devt = binfo.devt;
+       info->nr_sects = binfo.nr_sects;
+       info->start_sect = binfo.start_sect;
+
+       memset(&dev, 0, sizeof(dev));
+       dev.total_size = info->nr_sects << SECTOR_SHIFT;
+       dev.flags = info->flags;
+       dev.read = psblk_generic_blk_read;
+       dev.write = psblk_generic_blk_write;
+       dev.erase = NULL;
+       dev.panic_write = info->panic_write ? psblk_blk_panic_write : NULL;
+
+       ret = __register_pstore_device(&dev);
+       if (ret)
+               goto err_put_bdev;
+
+       bdevname(bdev, bdev_name);
+       pr_info("attached %s%s\n", bdev_name,
+               info->panic_write ? "" : " (no dedicated panic_write!)");
+       return 0;
+
+err_put_bdev:
+       psblk_bdev = NULL;
+       blkdev_panic_write = NULL;
+       psblk_put_bdev(bdev, holder);
+       return ret;
+}
+
+/**
+ * register_pstore_blk() - register block device to pstore/blk
+ *
+ * @info: details on the desired block device interface
+ *
+ * Return:
+ * * 0         - OK
+ * * Others    - something error.
+ */
+int register_pstore_blk(struct pstore_blk_info *info)
+{
+       int ret;
+
+       mutex_lock(&pstore_blk_lock);
+       ret = __register_pstore_blk(info);
+       mutex_unlock(&pstore_blk_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(register_pstore_blk);
+
+static void __unregister_pstore_blk(unsigned int major)
+{
+       struct pstore_device_info dev = { .read = psblk_generic_blk_read };
+       void *holder = blkdev;
+
+       lockdep_assert_held(&pstore_blk_lock);
+       if (psblk_bdev && MAJOR(psblk_bdev->bd_dev) == major) {
+               __unregister_pstore_device(&dev);
+               psblk_put_bdev(psblk_bdev, holder);
+               blkdev_panic_write = NULL;
+               psblk_bdev = NULL;
+       }
+}
+
+/**
+ * unregister_pstore_blk() - unregister block device from pstore/blk
+ *
+ * @major: the major device number of device
+ */
+void unregister_pstore_blk(unsigned int major)
+{
+       mutex_lock(&pstore_blk_lock);
+       __unregister_pstore_blk(major);
+       mutex_unlock(&pstore_blk_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_pstore_blk);
+
+/* get information of pstore/blk */
+int pstore_blk_get_config(struct pstore_blk_config *info)
+{
+       strncpy(info->device, blkdev, 80);
+       info->max_reason = max_reason;
+       info->kmsg_size = check_size(kmsg_size, 4096);
+       info->pmsg_size = check_size(pmsg_size, 4096);
+       info->ftrace_size = check_size(ftrace_size, 4096);
+       info->console_size = check_size(console_size, 4096);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pstore_blk_get_config);
+
+static int __init pstore_blk_init(void)
+{
+       struct pstore_blk_info info = { };
+       int ret = 0;
+
+       mutex_lock(&pstore_blk_lock);
+       if (!pstore_zone_info && best_effort && blkdev[0])
+               ret = __register_pstore_blk(&info);
+       mutex_unlock(&pstore_blk_lock);
+
+       return ret;
+}
+late_initcall(pstore_blk_init);
+
+static void __exit pstore_blk_exit(void)
+{
+       mutex_lock(&pstore_blk_lock);
+       if (psblk_bdev)
+               __unregister_pstore_blk(MAJOR(psblk_bdev->bd_dev));
+       else {
+               struct pstore_device_info dev = { };
+
+               if (pstore_zone_info)
+                       dev.read = pstore_zone_info->read;
+               __unregister_pstore_device(&dev);
+       }
+       mutex_unlock(&pstore_blk_lock);
+}
+module_exit(pstore_blk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
+MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("pstore backend for block devices");
index bfbfc269807020cc91b1cba4421bafc732e76999..5c045070129390aed6776c8f0272f8781592b7b0 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/debugfs.h>
 #include <linux/err.h>
 #include <linux/cache.h>
+#include <linux/slab.h>
 #include <asm/barrier.h>
 #include "internal.h"
 
@@ -132,3 +133,56 @@ void pstore_unregister_ftrace(void)
 
        debugfs_remove_recursive(pstore_ftrace_dir);
 }
+
+ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
+                                 const char *src_log, size_t src_log_size)
+{
+       size_t dest_size, src_size, total, dest_off, src_off;
+       size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
+       void *merged_buf;
+       struct pstore_ftrace_record *drec, *srec, *mrec;
+       size_t record_size = sizeof(struct pstore_ftrace_record);
+
+       dest_off = *dest_log_size % record_size;
+       dest_size = *dest_log_size - dest_off;
+
+       src_off = src_log_size % record_size;
+       src_size = src_log_size - src_off;
+
+       total = dest_size + src_size;
+       merged_buf = kmalloc(total, GFP_KERNEL);
+       if (!merged_buf)
+               return -ENOMEM;
+
+       drec = (struct pstore_ftrace_record *)(*dest_log + dest_off);
+       srec = (struct pstore_ftrace_record *)(src_log + src_off);
+       mrec = (struct pstore_ftrace_record *)(merged_buf);
+
+       while (dest_size > 0 && src_size > 0) {
+               if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
+                   pstore_ftrace_read_timestamp(&srec[src_idx])) {
+                       mrec[merged_idx++] = drec[dest_idx++];
+                       dest_size -= record_size;
+               } else {
+                       mrec[merged_idx++] = srec[src_idx++];
+                       src_size -= record_size;
+               }
+       }
+
+       while (dest_size > 0) {
+               mrec[merged_idx++] = drec[dest_idx++];
+               dest_size -= record_size;
+       }
+
+       while (src_size > 0) {
+               mrec[merged_idx++] = srec[src_idx++];
+               src_size -= record_size;
+       }
+
+       kfree(*dest_log);
+       *dest_log = merged_buf;
+       *dest_log_size = total;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pstore_ftrace_combine_log);
index d99b5d39aa90e04a8822196973bda309018c902e..c331efe8de953732b22a385473357ab9ea9f2123 100644 (file)
 #include <linux/magic.h>
 #include <linux/pstore.h>
 #include <linux/slab.h>
-#include <linux/spinlock.h>
 #include <linux/uaccess.h>
 
 #include "internal.h"
 
 #define        PSTORE_NAMELEN  64
 
-static DEFINE_SPINLOCK(allpstore_lock);
-static LIST_HEAD(allpstore);
+static DEFINE_MUTEX(records_list_lock);
+static LIST_HEAD(records_list);
+
+static DEFINE_MUTEX(pstore_sb_lock);
+static struct super_block *pstore_sb;
 
 struct pstore_private {
        struct list_head list;
+       struct dentry *dentry;
        struct pstore_record *record;
        size_t total_size;
 };
@@ -178,10 +181,22 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
 {
        struct pstore_private *p = d_inode(dentry)->i_private;
        struct pstore_record *record = p->record;
+       int rc = 0;
 
        if (!record->psi->erase)
                return -EPERM;
 
+       /* Make sure we can't race while removing this file. */
+       mutex_lock(&records_list_lock);
+       if (!list_empty(&p->list))
+               list_del_init(&p->list);
+       else
+               rc = -ENOENT;
+       p->dentry = NULL;
+       mutex_unlock(&records_list_lock);
+       if (rc)
+               return rc;
+
        mutex_lock(&record->psi->read_mutex);
        record->psi->erase(record);
        mutex_unlock(&record->psi->read_mutex);
@@ -192,15 +207,9 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
 static void pstore_evict_inode(struct inode *inode)
 {
        struct pstore_private   *p = inode->i_private;
-       unsigned long           flags;
 
        clear_inode(inode);
-       if (p) {
-               spin_lock_irqsave(&allpstore_lock, flags);
-               list_del(&p->list);
-               spin_unlock_irqrestore(&allpstore_lock, flags);
-               free_pstore_private(p);
-       }
+       free_pstore_private(p);
 }
 
 static const struct inode_operations pstore_dir_inode_operations = {
@@ -278,11 +287,54 @@ static const struct super_operations pstore_ops = {
        .show_options   = pstore_show_options,
 };
 
-static struct super_block *pstore_sb;
+static struct dentry *psinfo_lock_root(void)
+{
+       struct dentry *root;
 
-bool pstore_is_mounted(void)
+       mutex_lock(&pstore_sb_lock);
+       /*
+        * Having no backend is fine -- no records appear.
+        * Not being mounted is fine -- nothing to do.
+        */
+       if (!psinfo || !pstore_sb) {
+               mutex_unlock(&pstore_sb_lock);
+               return NULL;
+       }
+
+       root = pstore_sb->s_root;
+       inode_lock(d_inode(root));
+       mutex_unlock(&pstore_sb_lock);
+
+       return root;
+}
+
+int pstore_put_backend_records(struct pstore_info *psi)
 {
-       return pstore_sb != NULL;
+       struct pstore_private *pos, *tmp;
+       struct dentry *root;
+       int rc = 0;
+
+       root = psinfo_lock_root();
+       if (!root)
+               return 0;
+
+       mutex_lock(&records_list_lock);
+       list_for_each_entry_safe(pos, tmp, &records_list, list) {
+               if (pos->record->psi == psi) {
+                       list_del_init(&pos->list);
+                       rc = simple_unlink(d_inode(root), pos->dentry);
+                       if (WARN_ON(rc))
+                               break;
+                       d_drop(pos->dentry);
+                       dput(pos->dentry);
+                       pos->dentry = NULL;
+               }
+       }
+       mutex_unlock(&records_list_lock);
+
+       inode_unlock(d_inode(root));
+
+       return rc;
 }
 
 /*
@@ -297,23 +349,20 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
        int                     rc = 0;
        char                    name[PSTORE_NAMELEN];
        struct pstore_private   *private, *pos;
-       unsigned long           flags;
        size_t                  size = record->size + record->ecc_notice_size;
 
-       WARN_ON(!inode_is_locked(d_inode(root)));
+       if (WARN_ON(!inode_is_locked(d_inode(root))))
+               return -EINVAL;
 
-       spin_lock_irqsave(&allpstore_lock, flags);
-       list_for_each_entry(pos, &allpstore, list) {
+       rc = -EEXIST;
+       /* Skip records that are already present in the filesystem. */
+       mutex_lock(&records_list_lock);
+       list_for_each_entry(pos, &records_list, list) {
                if (pos->record->type == record->type &&
                    pos->record->id == record->id &&
-                   pos->record->psi == record->psi) {
-                       rc = -EEXIST;
-                       break;
-               }
+                   pos->record->psi == record->psi)
+                       goto fail;
        }
-       spin_unlock_irqrestore(&allpstore_lock, flags);
-       if (rc)
-               return rc;
 
        rc = -ENOMEM;
        inode = pstore_get_inode(root->d_sb);
@@ -334,6 +383,7 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
        if (!dentry)
                goto fail_private;
 
+       private->dentry = dentry;
        private->record = record;
        inode->i_size = private->total_size = size;
        inode->i_private = private;
@@ -343,9 +393,8 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
 
        d_add(dentry, inode);
 
-       spin_lock_irqsave(&allpstore_lock, flags);
-       list_add(&private->list, &allpstore);
-       spin_unlock_irqrestore(&allpstore_lock, flags);
+       list_add(&private->list, &records_list);
+       mutex_unlock(&records_list_lock);
 
        return 0;
 
@@ -353,8 +402,8 @@ fail_private:
        free_pstore_private(private);
 fail_inode:
        iput(inode);
-
 fail:
+       mutex_unlock(&records_list_lock);
        return rc;
 }
 
@@ -366,16 +415,13 @@ fail:
  */
 void pstore_get_records(int quiet)
 {
-       struct pstore_info *psi = psinfo;
        struct dentry *root;
 
-       if (!psi || !pstore_sb)
+       root = psinfo_lock_root();
+       if (!root)
                return;
 
-       root = pstore_sb->s_root;
-
-       inode_lock(d_inode(root));
-       pstore_get_backend_records(psi, root, quiet);
+       pstore_get_backend_records(psinfo, root, quiet);
        inode_unlock(d_inode(root));
 }
 
@@ -383,8 +429,6 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
 
-       pstore_sb = sb;
-
        sb->s_maxbytes          = MAX_LFS_FILESIZE;
        sb->s_blocksize         = PAGE_SIZE;
        sb->s_blocksize_bits    = PAGE_SHIFT;
@@ -405,6 +449,10 @@ static int pstore_fill_super(struct super_block *sb, void *data, int silent)
        if (!sb->s_root)
                return -ENOMEM;
 
+       mutex_lock(&pstore_sb_lock);
+       pstore_sb = sb;
+       mutex_unlock(&pstore_sb_lock);
+
        pstore_get_records(0);
 
        return 0;
@@ -418,8 +466,17 @@ static struct dentry *pstore_mount(struct file_system_type *fs_type,
 
 static void pstore_kill_sb(struct super_block *sb)
 {
+       mutex_lock(&pstore_sb_lock);
+       WARN_ON(pstore_sb != sb);
+
        kill_litter_super(sb);
        pstore_sb = NULL;
+
+       mutex_lock(&records_list_lock);
+       INIT_LIST_HEAD(&records_list);
+       mutex_unlock(&records_list_lock);
+
+       mutex_unlock(&pstore_sb_lock);
 }
 
 static struct file_system_type pstore_fs_type = {
index 7062ea4bc57c5b31cba5c19815f343e387dafa0c..7fb219042f13d149b10502f2969fb7603f27e62a 100644 (file)
@@ -12,9 +12,18 @@ extern unsigned long kmsg_bytes;
 #ifdef CONFIG_PSTORE_FTRACE
 extern void pstore_register_ftrace(void);
 extern void pstore_unregister_ftrace(void);
+ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
+                                 const char *src_log, size_t src_log_size);
 #else
 static inline void pstore_register_ftrace(void) {}
 static inline void pstore_unregister_ftrace(void) {}
+static inline ssize_t
+pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size,
+                         const char *src_log, size_t src_log_size)
+{
+       *dest_log_size = 0;
+       return 0;
+}
 #endif
 
 #ifdef CONFIG_PSTORE_PMSG
@@ -31,9 +40,9 @@ extern void   pstore_set_kmsg_bytes(int);
 extern void    pstore_get_records(int);
 extern void    pstore_get_backend_records(struct pstore_info *psi,
                                           struct dentry *root, int quiet);
+extern int     pstore_put_backend_records(struct pstore_info *psi);
 extern int     pstore_mkfile(struct dentry *root,
                              struct pstore_record *record);
-extern bool    pstore_is_mounted(void);
 extern void    pstore_record_init(struct pstore_record *record,
                                   struct pstore_info *psi);
 
index 408277ee3cdb9dfad303e958f00f6d9d08528970..a9e297eefdff24163dc30d02697214f2d81cd352 100644 (file)
@@ -44,7 +44,7 @@ static int pstore_update_ms = -1;
 module_param_named(update_ms, pstore_update_ms, int, 0600);
 MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content "
                 "(default is -1, which means runtime updates are disabled; "
-                "enabling this option is not safe, it may lead to further "
+                "enabling this option may not be safe; it may lead to further "
                 "corruption on Oopses)");
 
 /* Names should be in the same order as the enum pstore_type_id */
@@ -69,19 +69,25 @@ static void pstore_dowork(struct work_struct *);
 static DECLARE_WORK(pstore_work, pstore_dowork);
 
 /*
- * pstore_lock just protects "psinfo" during
- * calls to pstore_register()
+ * psinfo_lock protects "psinfo" during calls to
+ * pstore_register(), pstore_unregister(), and
+ * the filesystem mount/unmount routines.
  */
-static DEFINE_SPINLOCK(pstore_lock);
+static DEFINE_MUTEX(psinfo_lock);
 struct pstore_info *psinfo;
 
 static char *backend;
+module_param(backend, charp, 0444);
+MODULE_PARM_DESC(backend, "specific backend to use");
+
 static char *compress =
 #ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
                CONFIG_PSTORE_COMPRESS_DEFAULT;
 #else
                NULL;
 #endif
+module_param(compress, charp, 0444);
+MODULE_PARM_DESC(compress, "compression to use");
 
 /* Compression parameters */
 static struct crypto_comp *tfm;
@@ -129,24 +135,12 @@ enum pstore_type_id pstore_name_to_type(const char *name)
 }
 EXPORT_SYMBOL_GPL(pstore_name_to_type);
 
-static const char *get_reason_str(enum kmsg_dump_reason reason)
+static void pstore_timer_kick(void)
 {
-       switch (reason) {
-       case KMSG_DUMP_PANIC:
-               return "Panic";
-       case KMSG_DUMP_OOPS:
-               return "Oops";
-       case KMSG_DUMP_EMERG:
-               return "Emergency";
-       case KMSG_DUMP_RESTART:
-               return "Restart";
-       case KMSG_DUMP_HALT:
-               return "Halt";
-       case KMSG_DUMP_POWEROFF:
-               return "Poweroff";
-       default:
-               return "Unknown";
-       }
+       if (pstore_update_ms < 0)
+               return;
+
+       mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms));
 }
 
 /*
@@ -393,7 +387,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
        unsigned int    part = 1;
        int             ret;
 
-       why = get_reason_str(reason);
+       why = kmsg_dump_reason_str(reason);
 
        if (down_trylock(&psinfo->buf_lock)) {
                /* Failed to acquire lock: give up if we cannot wait. */
@@ -459,8 +453,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                }
 
                ret = psinfo->write(&record);
-               if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
+               if (ret == 0 && reason == KMSG_DUMP_OOPS) {
                        pstore_new_entry = 1;
+                       pstore_timer_kick();
+               }
 
                total += record.size;
                part++;
@@ -503,14 +499,20 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
 }
 
 static struct console pstore_console = {
-       .name   = "pstore",
        .write  = pstore_console_write,
-       .flags  = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
        .index  = -1,
 };
 
 static void pstore_register_console(void)
 {
+       /* Show which backend is going to get console writes. */
+       strscpy(pstore_console.name, psinfo->name,
+               sizeof(pstore_console.name));
+       /*
+        * Always initialize flags here since prior unregister_console()
+        * calls may have changed settings (specifically CON_ENABLED).
+        */
+       pstore_console.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME;
        register_console(&pstore_console);
 }
 
@@ -555,8 +557,6 @@ out:
  */
 int pstore_register(struct pstore_info *psi)
 {
-       struct module *owner = psi->owner;
-
        if (backend && strcmp(backend, psi->name)) {
                pr_warn("ignoring unexpected backend '%s'\n", psi->name);
                return -EPERM;
@@ -576,11 +576,11 @@ int pstore_register(struct pstore_info *psi)
                return -EINVAL;
        }
 
-       spin_lock(&pstore_lock);
+       mutex_lock(&psinfo_lock);
        if (psinfo) {
                pr_warn("backend '%s' already loaded: ignoring '%s'\n",
                        psinfo->name, psi->name);
-               spin_unlock(&pstore_lock);
+               mutex_unlock(&psinfo_lock);
                return -EBUSY;
        }
 
@@ -589,21 +589,16 @@ int pstore_register(struct pstore_info *psi)
        psinfo = psi;
        mutex_init(&psinfo->read_mutex);
        sema_init(&psinfo->buf_lock, 1);
-       spin_unlock(&pstore_lock);
-
-       if (owner && !try_module_get(owner)) {
-               psinfo = NULL;
-               return -EINVAL;
-       }
 
        if (psi->flags & PSTORE_FLAGS_DMESG)
                allocate_buf_for_compression();
 
-       if (pstore_is_mounted())
-               pstore_get_records(0);
+       pstore_get_records(0);
 
-       if (psi->flags & PSTORE_FLAGS_DMESG)
+       if (psi->flags & PSTORE_FLAGS_DMESG) {
+               pstore_dumper.max_reason = psinfo->max_reason;
                pstore_register_kmsg();
+       }
        if (psi->flags & PSTORE_FLAGS_CONSOLE)
                pstore_register_console();
        if (psi->flags & PSTORE_FLAGS_FTRACE)
@@ -612,33 +607,36 @@ int pstore_register(struct pstore_info *psi)
                pstore_register_pmsg();
 
        /* Start watching for new records, if desired. */
-       if (pstore_update_ms >= 0) {
-               pstore_timer.expires = jiffies +
-                       msecs_to_jiffies(pstore_update_ms);
-               add_timer(&pstore_timer);
-       }
+       pstore_timer_kick();
 
        /*
         * Update the module parameter backend, so it is visible
         * through /sys/module/pstore/parameters/backend
         */
-       backend = psi->name;
+       backend = kstrdup(psi->name, GFP_KERNEL);
 
        pr_info("Registered %s as persistent store backend\n", psi->name);
 
-       module_put(owner);
-
+       mutex_unlock(&psinfo_lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(pstore_register);
 
 void pstore_unregister(struct pstore_info *psi)
 {
-       /* Stop timer and make sure all work has finished. */
-       pstore_update_ms = -1;
-       del_timer_sync(&pstore_timer);
-       flush_work(&pstore_work);
+       /* It's okay to unregister nothing. */
+       if (!psi)
+               return;
+
+       mutex_lock(&psinfo_lock);
+
+       /* Only one backend can be registered at a time. */
+       if (WARN_ON(psi != psinfo)) {
+               mutex_unlock(&psinfo_lock);
+               return;
+       }
 
+       /* Unregister all callbacks. */
        if (psi->flags & PSTORE_FLAGS_PMSG)
                pstore_unregister_pmsg();
        if (psi->flags & PSTORE_FLAGS_FTRACE)
@@ -648,10 +646,19 @@ void pstore_unregister(struct pstore_info *psi)
        if (psi->flags & PSTORE_FLAGS_DMESG)
                pstore_unregister_kmsg();
 
+       /* Stop timer and make sure all work has finished. */
+       del_timer_sync(&pstore_timer);
+       flush_work(&pstore_work);
+
+       /* Remove all backend records from filesystem tree. */
+       pstore_put_backend_records(psi);
+
        free_buf_for_compression();
 
        psinfo = NULL;
+       kfree(backend);
        backend = NULL;
+       mutex_unlock(&psinfo_lock);
 }
 EXPORT_SYMBOL_GPL(pstore_unregister);
 
@@ -788,9 +795,7 @@ static void pstore_timefunc(struct timer_list *unused)
                schedule_work(&pstore_work);
        }
 
-       if (pstore_update_ms >= 0)
-               mod_timer(&pstore_timer,
-                         jiffies + msecs_to_jiffies(pstore_update_ms));
+       pstore_timer_kick();
 }
 
 static void __init pstore_choose_compression(void)
@@ -835,11 +840,5 @@ static void __exit pstore_exit(void)
 }
 module_exit(pstore_exit)
 
-module_param(compress, charp, 0444);
-MODULE_PARM_DESC(compress, "Pstore compression to use");
-
-module_param(backend, charp, 0444);
-MODULE_PARM_DESC(backend, "Pstore backend to use");
-
 MODULE_AUTHOR("Tony Luck <tony.luck@intel.com>");
 MODULE_LICENSE("GPL");
index 795622190c01492e4fbd1a05b364d27fc8823003..ca6d8a86728566520c896cda6b7b7986c3e35e36 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/pstore_ram.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include "internal.h"
 
 #define RAMOOPS_KERNMSG_HDR "===="
 #define MIN_MEM_SIZE 4096UL
@@ -53,22 +54,27 @@ MODULE_PARM_DESC(mem_size,
                "size of reserved RAM used to store oops/panic logs");
 
 static unsigned int mem_type;
-module_param(mem_type, uint, 0600);
+module_param(mem_type, uint, 0400);
 MODULE_PARM_DESC(mem_type,
                "set to 1 to try to use unbuffered memory (default 0)");
 
-static int dump_oops = 1;
-module_param(dump_oops, int, 0600);
-MODULE_PARM_DESC(dump_oops,
-               "set to 1 to dump oopses, 0 to only dump panics (default 1)");
+static int ramoops_max_reason = -1;
+module_param_named(max_reason, ramoops_max_reason, int, 0400);
+MODULE_PARM_DESC(max_reason,
+                "maximum reason for kmsg dump (default 2: Oops and Panic) ");
 
 static int ramoops_ecc;
-module_param_named(ecc, ramoops_ecc, int, 0600);
+module_param_named(ecc, ramoops_ecc, int, 0400);
 MODULE_PARM_DESC(ramoops_ecc,
                "if non-zero, the option enables ECC support and specifies "
                "ECC buffer size in bytes (1 is a special value, means 16 "
                "bytes ECC)");
 
+static int ramoops_dump_oops = -1;
+module_param_named(dump_oops, ramoops_dump_oops, int, 0400);
+MODULE_PARM_DESC(dump_oops,
+                "(deprecated: use max_reason instead) set to 1 to dump oopses & panics, 0 to only dump panics");
+
 struct ramoops_context {
        struct persistent_ram_zone **dprzs;     /* Oops dump zones */
        struct persistent_ram_zone *cprz;       /* Console zone */
@@ -81,7 +87,6 @@ struct ramoops_context {
        size_t console_size;
        size_t ftrace_size;
        size_t pmsg_size;
-       int dump_oops;
        u32 flags;
        struct persistent_ram_ecc_info ecc_info;
        unsigned int max_dump_cnt;
@@ -168,58 +173,6 @@ static bool prz_ok(struct persistent_ram_zone *prz)
                           persistent_ram_ecc_string(prz, NULL, 0));
 }
 
-static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
-                                 struct persistent_ram_zone *src)
-{
-       size_t dest_size, src_size, total, dest_off, src_off;
-       size_t dest_idx = 0, src_idx = 0, merged_idx = 0;
-       void *merged_buf;
-       struct pstore_ftrace_record *drec, *srec, *mrec;
-       size_t record_size = sizeof(struct pstore_ftrace_record);
-
-       dest_off = dest->old_log_size % record_size;
-       dest_size = dest->old_log_size - dest_off;
-
-       src_off = src->old_log_size % record_size;
-       src_size = src->old_log_size - src_off;
-
-       total = dest_size + src_size;
-       merged_buf = kmalloc(total, GFP_KERNEL);
-       if (!merged_buf)
-               return -ENOMEM;
-
-       drec = (struct pstore_ftrace_record *)(dest->old_log + dest_off);
-       srec = (struct pstore_ftrace_record *)(src->old_log + src_off);
-       mrec = (struct pstore_ftrace_record *)(merged_buf);
-
-       while (dest_size > 0 && src_size > 0) {
-               if (pstore_ftrace_read_timestamp(&drec[dest_idx]) <
-                   pstore_ftrace_read_timestamp(&srec[src_idx])) {
-                       mrec[merged_idx++] = drec[dest_idx++];
-                       dest_size -= record_size;
-               } else {
-                       mrec[merged_idx++] = srec[src_idx++];
-                       src_size -= record_size;
-               }
-       }
-
-       while (dest_size > 0) {
-               mrec[merged_idx++] = drec[dest_idx++];
-               dest_size -= record_size;
-       }
-
-       while (src_size > 0) {
-               mrec[merged_idx++] = srec[src_idx++];
-               src_size -= record_size;
-       }
-
-       kfree(dest->old_log);
-       dest->old_log = merged_buf;
-       dest->old_log_size = total;
-
-       return 0;
-}
-
 static ssize_t ramoops_pstore_read(struct pstore_record *record)
 {
        ssize_t size = 0;
@@ -291,7 +244,12 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
                                tmp_prz->corrected_bytes +=
                                                prz_next->corrected_bytes;
                                tmp_prz->bad_blocks += prz_next->bad_blocks;
-                               size = ftrace_log_combine(tmp_prz, prz_next);
+
+                               size = pstore_ftrace_combine_log(
+                                               &tmp_prz->old_log,
+                                               &tmp_prz->old_log_size,
+                                               prz_next->old_log,
+                                               prz_next->old_log_size);
                                if (size)
                                        goto out;
                        }
@@ -382,16 +340,14 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
                return -EINVAL;
 
        /*
-        * Out of the various dmesg dump types, ramoops is currently designed
-        * to only store crash logs, rather than storing general kernel logs.
+        * We could filter on record->reason here if we wanted to (which
+        * would duplicate what happened before the "max_reason" setting
+        * was added), but that would defeat the purpose of a system
+        * changing printk.always_kmsg_dump, so instead log everything that
+        * the kmsg dumper sends us, since it should be doing the filtering
+        * based on the combination of printk.always_kmsg_dump and our
+        * requested "max_reason".
         */
-       if (record->reason != KMSG_DUMP_OOPS &&
-           record->reason != KMSG_DUMP_PANIC)
-               return -EINVAL;
-
-       /* Skip Oopes when configured to do so. */
-       if (record->reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
-               return -EINVAL;
 
        /*
         * Explicitly only take the first part of any new crash.
@@ -644,19 +600,25 @@ static int ramoops_init_prz(const char *name,
        return 0;
 }
 
-static int ramoops_parse_dt_size(struct platform_device *pdev,
-                                const char *propname, u32 *value)
+/* Read a u32 from a dt property and make sure it's safe for an int. */
+static int ramoops_parse_dt_u32(struct platform_device *pdev,
+                               const char *propname,
+                               u32 default_value, u32 *value)
 {
        u32 val32 = 0;
        int ret;
 
        ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
-       if (ret < 0 && ret != -EINVAL) {
+       if (ret == -EINVAL) {
+               /* field is missing, use default value. */
+               val32 = default_value;
+       } else if (ret < 0) {
                dev_err(&pdev->dev, "failed to parse property %s: %d\n",
                        propname, ret);
                return ret;
        }
 
+       /* Sanity check our results. */
        if (val32 > INT_MAX) {
                dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
                return -EOVERFLOW;
@@ -687,23 +649,32 @@ static int ramoops_parse_dt(struct platform_device *pdev,
        pdata->mem_size = resource_size(res);
        pdata->mem_address = res->start;
        pdata->mem_type = of_property_read_bool(of_node, "unbuffered");
-       pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops");
-
-#define parse_size(name, field) {                                      \
-               ret = ramoops_parse_dt_size(pdev, name, &value);        \
+       /*
+        * Setting "no-dump-oops" is deprecated and will be ignored if
+        * "max_reason" is also specified.
+        */
+       if (of_property_read_bool(of_node, "no-dump-oops"))
+               pdata->max_reason = KMSG_DUMP_PANIC;
+       else
+               pdata->max_reason = KMSG_DUMP_OOPS;
+
+#define parse_u32(name, field, default_value) {                                \
+               ret = ramoops_parse_dt_u32(pdev, name, default_value,   \
+                                           &value);                    \
                if (ret < 0)                                            \
                        return ret;                                     \
                field = value;                                          \
        }
 
-       parse_size("record-size", pdata->record_size);
-       parse_size("console-size", pdata->console_size);
-       parse_size("ftrace-size", pdata->ftrace_size);
-       parse_size("pmsg-size", pdata->pmsg_size);
-       parse_size("ecc-size", pdata->ecc_info.ecc_size);
-       parse_size("flags", pdata->flags);
+       parse_u32("record-size", pdata->record_size, 0);
+       parse_u32("console-size", pdata->console_size, 0);
+       parse_u32("ftrace-size", pdata->ftrace_size, 0);
+       parse_u32("pmsg-size", pdata->pmsg_size, 0);
+       parse_u32("ecc-size", pdata->ecc_info.ecc_size, 0);
+       parse_u32("flags", pdata->flags, 0);
+       parse_u32("max-reason", pdata->max_reason, pdata->max_reason);
 
-#undef parse_size
+#undef parse_u32
 
        /*
         * Some old Chromebooks relied on the kernel setting the
@@ -785,7 +756,6 @@ static int ramoops_probe(struct platform_device *pdev)
        cxt->console_size = pdata->console_size;
        cxt->ftrace_size = pdata->ftrace_size;
        cxt->pmsg_size = pdata->pmsg_size;
-       cxt->dump_oops = pdata->dump_oops;
        cxt->flags = pdata->flags;
        cxt->ecc_info = pdata->ecc_info;
 
@@ -828,8 +798,10 @@ static int ramoops_probe(struct platform_device *pdev)
         * the single region size is how to check.
         */
        cxt->pstore.flags = 0;
-       if (cxt->max_dump_cnt)
+       if (cxt->max_dump_cnt) {
                cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
+               cxt->pstore.max_reason = pdata->max_reason;
+       }
        if (cxt->console_size)
                cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;
        if (cxt->max_ftrace_cnt)
@@ -865,7 +837,7 @@ static int ramoops_probe(struct platform_device *pdev)
        mem_size = pdata->mem_size;
        mem_address = pdata->mem_address;
        record_size = pdata->record_size;
-       dump_oops = pdata->dump_oops;
+       ramoops_max_reason = pdata->max_reason;
        ramoops_console_size = pdata->console_size;
        ramoops_pmsg_size = pdata->pmsg_size;
        ramoops_ftrace_size = pdata->ftrace_size;
@@ -948,7 +920,16 @@ static void __init ramoops_register_dummy(void)
        pdata.console_size = ramoops_console_size;
        pdata.ftrace_size = ramoops_ftrace_size;
        pdata.pmsg_size = ramoops_pmsg_size;
-       pdata.dump_oops = dump_oops;
+       /* If "max_reason" is set, its value has priority over "dump_oops". */
+       if (ramoops_max_reason >= 0)
+               pdata.max_reason = ramoops_max_reason;
+       /* Otherwise, if "dump_oops" is set, parse it into "max_reason". */
+       else if (ramoops_dump_oops != -1)
+               pdata.max_reason = ramoops_dump_oops ? KMSG_DUMP_OOPS
+                                                    : KMSG_DUMP_PANIC;
+       /* And if neither are explicitly set, use the default. */
+       else
+               pdata.max_reason = KMSG_DUMP_OOPS;
        pdata.flags = RAMOOPS_FLAG_FTRACE_PER_CPU;
 
        /*
index c917c191e78c6860d0ba08d208618499178b1eb1..aa8e0b65ff1ae07d67e19bba51a3b5e872e006db 100644 (file)
@@ -283,7 +283,7 @@ static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
        const void __user *s, unsigned int start, unsigned int count)
 {
        struct persistent_ram_buffer *buffer = prz->buffer;
-       int ret = unlikely(__copy_from_user(buffer->data + start, s, count)) ?
+       int ret = unlikely(copy_from_user(buffer->data + start, s, count)) ?
                -EFAULT : 0;
        persistent_ram_update_ecc(prz, start, count);
        return ret;
@@ -348,8 +348,6 @@ int notrace persistent_ram_write_user(struct persistent_ram_zone *prz,
        int rem, ret = 0, c = count;
        size_t start;
 
-       if (unlikely(!access_ok(s, count)))
-               return -EFAULT;
        if (unlikely(c > prz->buffer_size)) {
                s += c - prz->buffer_size;
                c = prz->buffer_size;
diff --git a/fs/pstore/zone.c b/fs/pstore/zone.c
new file mode 100644 (file)
index 0000000..819428d
--- /dev/null
@@ -0,0 +1,1465 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Provide a pstore intermediate backend, organized into kernel memory
+ * allocated zones that are then mapped and flushed into a single
+ * contiguous region on a storage backend of some kind (block, mtd, etc).
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mount.h>
+#include <linux/printk.h>
+#include <linux/fs.h>
+#include <linux/pstore_zone.h>
+#include <linux/kdev_t.h>
+#include <linux/device.h>
+#include <linux/namei.h>
+#include <linux/fcntl.h>
+#include <linux/uio.h>
+#include <linux/writeback.h>
+#include "internal.h"
+
+/**
+ * struct psz_head - header of zone to flush to storage
+ *
+ * @sig: signature to indicate header (PSZ_SIG xor PSZONE-type value)
+ * @datalen: length of data in @data
+ * @start: offset into @data where the beginning of the stored bytes begin
+ * @data: zone data.
+ */
+struct psz_buffer {
+#define PSZ_SIG (0x43474244) /* DBGC */
+       uint32_t sig;
+       atomic_t datalen;
+       atomic_t start;
+       uint8_t data[];
+};
+
+/**
+ * struct psz_kmsg_header - kmsg dump-specific header to flush to storage
+ *
+ * @magic: magic num for kmsg dump header
+ * @time: kmsg dump trigger time
+ * @compressed: whether conpressed
+ * @counter: kmsg dump counter
+ * @reason: the kmsg dump reason (e.g. oops, panic, etc)
+ * @data: pointer to log data
+ *
+ * This is a sub-header for a kmsg dump, trailing after &psz_buffer.
+ */
+struct psz_kmsg_header {
+#define PSTORE_KMSG_HEADER_MAGIC 0x4dfc3ae5 /* Just a random number */
+       uint32_t magic;
+       struct timespec64 time;
+       bool compressed;
+       uint32_t counter;
+       enum kmsg_dump_reason reason;
+       uint8_t data[];
+};
+
+/**
+ * struct pstore_zone - single stored buffer
+ *
+ * @off: zone offset of storage
+ * @type: front-end type for this zone
+ * @name: front-end name for this zone
+ * @buffer: pointer to data buffer managed by this zone
+ * @oldbuf: pointer to old data buffer
+ * @buffer_size: bytes in @buffer->data
+ * @should_recover: whether this zone should recover from storage
+ * @dirty: whether the data in @buffer dirty
+ *
+ * zone structure in memory.
+ */
+struct pstore_zone {
+       loff_t off;
+       const char *name;
+       enum pstore_type_id type;
+
+       struct psz_buffer *buffer;
+       struct psz_buffer *oldbuf;
+       size_t buffer_size;
+       bool should_recover;
+       atomic_t dirty;
+};
+
+/**
+ * struct psz_context - all about running state of pstore/zone
+ *
+ * @kpszs: kmsg dump storage zones
+ * @ppsz: pmsg storage zone
+ * @cpsz: console storage zone
+ * @fpszs: ftrace storage zones
+ * @kmsg_max_cnt: max count of @kpszs
+ * @kmsg_read_cnt: counter of total read kmsg dumps
+ * @kmsg_write_cnt: counter of total kmsg dump writes
+ * @pmsg_read_cnt: counter of total read pmsg zone
+ * @console_read_cnt: counter of total read console zone
+ * @ftrace_max_cnt: max count of @fpszs
+ * @ftrace_read_cnt: counter of max read ftrace zone
+ * @oops_counter: counter of oops dumps
+ * @panic_counter: counter of panic dumps
+ * @recovered: whether finished recovering data from storage
+ * @on_panic: whether panic is happening
+ * @pstore_zone_info_lock: lock to @pstore_zone_info
+ * @pstore_zone_info: information from backend
+ * @pstore: structure for pstore
+ */
+struct psz_context {
+       struct pstore_zone **kpszs;
+       struct pstore_zone *ppsz;
+       struct pstore_zone *cpsz;
+       struct pstore_zone **fpszs;
+       unsigned int kmsg_max_cnt;
+       unsigned int kmsg_read_cnt;
+       unsigned int kmsg_write_cnt;
+       unsigned int pmsg_read_cnt;
+       unsigned int console_read_cnt;
+       unsigned int ftrace_max_cnt;
+       unsigned int ftrace_read_cnt;
+       /*
+        * These counters should be calculated during recovery.
+        * It records the oops/panic times after crashes rather than boots.
+        */
+       unsigned int oops_counter;
+       unsigned int panic_counter;
+       atomic_t recovered;
+       atomic_t on_panic;
+
+       /*
+        * pstore_zone_info_lock protects this entire structure during calls
+        * to register_pstore_zone()/unregister_pstore_zone().
+        */
+       struct mutex pstore_zone_info_lock;
+       struct pstore_zone_info *pstore_zone_info;
+       struct pstore_info pstore;
+};
+static struct psz_context pstore_zone_cxt;
+
+static void psz_flush_all_dirty_zones(struct work_struct *);
+static DECLARE_DELAYED_WORK(psz_cleaner, psz_flush_all_dirty_zones);
+
+/**
+ * enum psz_flush_mode - flush mode for psz_zone_write()
+ *
+ * @FLUSH_NONE: do not flush to storage but update data on memory
+ * @FLUSH_PART: just flush part of data including meta data to storage
+ * @FLUSH_META: just flush meta data of zone to storage
+ * @FLUSH_ALL: flush all of zone
+ */
+enum psz_flush_mode {
+       FLUSH_NONE = 0,
+       FLUSH_PART,
+       FLUSH_META,
+       FLUSH_ALL,
+};
+
+static inline int buffer_datalen(struct pstore_zone *zone)
+{
+       return atomic_read(&zone->buffer->datalen);
+}
+
+static inline int buffer_start(struct pstore_zone *zone)
+{
+       return atomic_read(&zone->buffer->start);
+}
+
+static inline bool is_on_panic(void)
+{
+       return atomic_read(&pstore_zone_cxt.on_panic);
+}
+
+static ssize_t psz_zone_read_buffer(struct pstore_zone *zone, char *buf,
+               size_t len, unsigned long off)
+{
+       if (!buf || !zone || !zone->buffer)
+               return -EINVAL;
+       if (off > zone->buffer_size)
+               return -EINVAL;
+       len = min_t(size_t, len, zone->buffer_size - off);
+       memcpy(buf, zone->buffer->data + off, len);
+       return len;
+}
+
+static int psz_zone_read_oldbuf(struct pstore_zone *zone, char *buf,
+               size_t len, unsigned long off)
+{
+       if (!buf || !zone || !zone->oldbuf)
+               return -EINVAL;
+       if (off > zone->buffer_size)
+               return -EINVAL;
+       len = min_t(size_t, len, zone->buffer_size - off);
+       memcpy(buf, zone->oldbuf->data + off, len);
+       return 0;
+}
+
+static int psz_zone_write(struct pstore_zone *zone,
+               enum psz_flush_mode flush_mode, const char *buf,
+               size_t len, unsigned long off)
+{
+       struct pstore_zone_info *info = pstore_zone_cxt.pstore_zone_info;
+       ssize_t wcnt = 0;
+       ssize_t (*writeop)(const char *buf, size_t bytes, loff_t pos);
+       size_t wlen;
+
+       if (off > zone->buffer_size)
+               return -EINVAL;
+
+       wlen = min_t(size_t, len, zone->buffer_size - off);
+       if (buf && wlen) {
+               memcpy(zone->buffer->data + off, buf, wlen);
+               atomic_set(&zone->buffer->datalen, wlen + off);
+       }
+
+       /* avoid to damage old records */
+       if (!is_on_panic() && !atomic_read(&pstore_zone_cxt.recovered))
+               goto dirty;
+
+       writeop = is_on_panic() ? info->panic_write : info->write;
+       if (!writeop)
+               goto dirty;
+
+       switch (flush_mode) {
+       case FLUSH_NONE:
+               if (unlikely(buf && wlen))
+                       goto dirty;
+               return 0;
+       case FLUSH_PART:
+               wcnt = writeop((const char *)zone->buffer->data + off, wlen,
+                               zone->off + sizeof(*zone->buffer) + off);
+               if (wcnt != wlen)
+                       goto dirty;
+               fallthrough;
+       case FLUSH_META:
+               wlen = sizeof(struct psz_buffer);
+               wcnt = writeop((const char *)zone->buffer, wlen, zone->off);
+               if (wcnt != wlen)
+                       goto dirty;
+               break;
+       case FLUSH_ALL:
+               wlen = zone->buffer_size + sizeof(*zone->buffer);
+               wcnt = writeop((const char *)zone->buffer, wlen, zone->off);
+               if (wcnt != wlen)
+                       goto dirty;
+               break;
+       }
+
+       return 0;
+dirty:
+       /* no need to mark dirty if going to try next zone */
+       if (wcnt == -ENOMSG)
+               return -ENOMSG;
+       atomic_set(&zone->dirty, true);
+       /* flush dirty zones nicely */
+       if (wcnt == -EBUSY && !is_on_panic())
+               schedule_delayed_work(&psz_cleaner, msecs_to_jiffies(500));
+       return -EBUSY;
+}
+
+static int psz_flush_dirty_zone(struct pstore_zone *zone)
+{
+       int ret;
+
+       if (unlikely(!zone))
+               return -EINVAL;
+
+       if (unlikely(!atomic_read(&pstore_zone_cxt.recovered)))
+               return -EBUSY;
+
+       if (!atomic_xchg(&zone->dirty, false))
+               return 0;
+
+       ret = psz_zone_write(zone, FLUSH_ALL, NULL, 0, 0);
+       if (ret)
+               atomic_set(&zone->dirty, true);
+       return ret;
+}
+
+static int psz_flush_dirty_zones(struct pstore_zone **zones, unsigned int cnt)
+{
+       int i, ret;
+       struct pstore_zone *zone;
+
+       if (!zones)
+               return -EINVAL;
+
+       for (i = 0; i < cnt; i++) {
+               zone = zones[i];
+               if (!zone)
+                       return -EINVAL;
+               ret = psz_flush_dirty_zone(zone);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static int psz_move_zone(struct pstore_zone *old, struct pstore_zone *new)
+{
+       const char *data = (const char *)old->buffer->data;
+       int ret;
+
+       ret = psz_zone_write(new, FLUSH_ALL, data, buffer_datalen(old), 0);
+       if (ret) {
+               atomic_set(&new->buffer->datalen, 0);
+               atomic_set(&new->dirty, false);
+               return ret;
+       }
+       atomic_set(&old->buffer->datalen, 0);
+       return 0;
+}
+
+static void psz_flush_all_dirty_zones(struct work_struct *work)
+{
+       struct psz_context *cxt = &pstore_zone_cxt;
+       int ret = 0;
+
+       if (cxt->ppsz)
+               ret |= psz_flush_dirty_zone(cxt->ppsz);
+       if (cxt->cpsz)
+               ret |= psz_flush_dirty_zone(cxt->cpsz);
+       if (cxt->kpszs)
+               ret |= psz_flush_dirty_zones(cxt->kpszs, cxt->kmsg_max_cnt);
+       if (cxt->fpszs)
+               ret |= psz_flush_dirty_zones(cxt->fpszs, cxt->ftrace_max_cnt);
+       if (ret && cxt->pstore_zone_info)
+               schedule_delayed_work(&psz_cleaner, msecs_to_jiffies(1000));
+}
+
+static int psz_kmsg_recover_data(struct psz_context *cxt)
+{
+       struct pstore_zone_info *info = cxt->pstore_zone_info;
+       struct pstore_zone *zone = NULL;
+       struct psz_buffer *buf;
+       unsigned long i;
+       ssize_t rcnt;
+
+       if (!info->read)
+               return -EINVAL;
+
+       for (i = 0; i < cxt->kmsg_max_cnt; i++) {
+               zone = cxt->kpszs[i];
+               if (unlikely(!zone))
+                       return -EINVAL;
+               if (atomic_read(&zone->dirty)) {
+                       unsigned int wcnt = cxt->kmsg_write_cnt;
+                       struct pstore_zone *new = cxt->kpszs[wcnt];
+                       int ret;
+
+                       ret = psz_move_zone(zone, new);
+                       if (ret) {
+                               pr_err("move zone from %lu to %d failed\n",
+                                               i, wcnt);
+                               return ret;
+                       }
+                       cxt->kmsg_write_cnt = (wcnt + 1) % cxt->kmsg_max_cnt;
+               }
+               if (!zone->should_recover)
+                       continue;
+               buf = zone->buffer;
+               rcnt = info->read((char *)buf, zone->buffer_size + sizeof(*buf),
+                               zone->off);
+               if (rcnt != zone->buffer_size + sizeof(*buf))
+                       return (int)rcnt < 0 ? (int)rcnt : -EIO;
+       }
+       return 0;
+}
+
+static int psz_kmsg_recover_meta(struct psz_context *cxt)
+{
+       struct pstore_zone_info *info = cxt->pstore_zone_info;
+       struct pstore_zone *zone;
+       size_t rcnt, len;
+       struct psz_buffer *buf;
+       struct psz_kmsg_header *hdr;
+       struct timespec64 time = { };
+       unsigned long i;
+       /*
+        * Recover may on panic, we can't allocate any memory by kmalloc.
+        * So, we use local array instead.
+        */
+       char buffer_header[sizeof(*buf) + sizeof(*hdr)] = {0};
+
+       if (!info->read)
+               return -EINVAL;
+
+       len = sizeof(*buf) + sizeof(*hdr);
+       buf = (struct psz_buffer *)buffer_header;
+       for (i = 0; i < cxt->kmsg_max_cnt; i++) {
+               zone = cxt->kpszs[i];
+               if (unlikely(!zone))
+                       return -EINVAL;
+
+               rcnt = info->read((char *)buf, len, zone->off);
+               if (rcnt == -ENOMSG) {
+                       pr_debug("%s with id %lu may be broken, skip\n",
+                                       zone->name, i);
+                       continue;
+               } else if (rcnt != len) {
+                       pr_err("read %s with id %lu failed\n", zone->name, i);
+                       return (int)rcnt < 0 ? (int)rcnt : -EIO;
+               }
+
+               if (buf->sig != zone->buffer->sig) {
+                       pr_debug("no valid data in kmsg dump zone %lu\n", i);
+                       continue;
+               }
+
+               if (zone->buffer_size < atomic_read(&buf->datalen)) {
+                       pr_info("found overtop zone: %s: id %lu, off %lld, size %zu\n",
+                                       zone->name, i, zone->off,
+                                       zone->buffer_size);
+                       continue;
+               }
+
+               hdr = (struct psz_kmsg_header *)buf->data;
+               if (hdr->magic != PSTORE_KMSG_HEADER_MAGIC) {
+                       pr_info("found invalid zone: %s: id %lu, off %lld, size %zu\n",
+                                       zone->name, i, zone->off,
+                                       zone->buffer_size);
+                       continue;
+               }
+
+               /*
+                * we get the newest zone, and the next one must be the oldest
+                * or unused zone, because we do write one by one like a circle.
+                */
+               if (hdr->time.tv_sec >= time.tv_sec) {
+                       time.tv_sec = hdr->time.tv_sec;
+                       cxt->kmsg_write_cnt = (i + 1) % cxt->kmsg_max_cnt;
+               }
+
+               if (hdr->reason == KMSG_DUMP_OOPS)
+                       cxt->oops_counter =
+                               max(cxt->oops_counter, hdr->counter);
+               else if (hdr->reason == KMSG_DUMP_PANIC)
+                       cxt->panic_counter =
+                               max(cxt->panic_counter, hdr->counter);
+
+               if (!atomic_read(&buf->datalen)) {
+                       pr_debug("found erased zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
+                                       zone->name, i, zone->off,
+                                       zone->buffer_size,
+                                       atomic_read(&buf->datalen));
+                       continue;
+               }
+
+               if (!is_on_panic())
+                       zone->should_recover = true;
+               pr_debug("found nice zone: %s: id %lu, off %lld, size %zu, datalen %d\n",
+                               zone->name, i, zone->off,
+                               zone->buffer_size, atomic_read(&buf->datalen));
+       }
+
+       return 0;
+}
+
+static int psz_kmsg_recover(struct psz_context *cxt)
+{
+       int ret;
+
+       if (!cxt->kpszs)
+               return 0;
+
+       ret = psz_kmsg_recover_meta(cxt);
+       if (ret)
+               goto recover_fail;
+
+       ret = psz_kmsg_recover_data(cxt);
+       if (ret)
+               goto recover_fail;
+
+       return 0;
+recover_fail:
+       pr_debug("psz_recover_kmsg failed\n");
+       return ret;
+}
+
+static int psz_recover_zone(struct psz_context *cxt, struct pstore_zone *zone)
+{
+       struct pstore_zone_info *info = cxt->pstore_zone_info;
+       struct psz_buffer *oldbuf, tmpbuf;
+       int ret = 0;
+       char *buf;
+       ssize_t rcnt, len, start, off;
+
+       if (!zone || zone->oldbuf)
+               return 0;
+
+       if (is_on_panic()) {
+               /* save data as much as possible */
+               psz_flush_dirty_zone(zone);
+               return 0;
+       }
+
+       if (unlikely(!info->read))
+               return -EINVAL;
+
+       len = sizeof(struct psz_buffer);
+       rcnt = info->read((char *)&tmpbuf, len, zone->off);
+       if (rcnt != len) {
+               pr_debug("read zone %s failed\n", zone->name);
+               return (int)rcnt < 0 ? (int)rcnt : -EIO;
+       }
+
+       if (tmpbuf.sig != zone->buffer->sig) {
+               pr_debug("no valid data in zone %s\n", zone->name);
+               return 0;
+       }
+
+       if (zone->buffer_size < atomic_read(&tmpbuf.datalen) ||
+               zone->buffer_size < atomic_read(&tmpbuf.start)) {
+               pr_info("found overtop zone: %s: off %lld, size %zu\n",
+                               zone->name, zone->off, zone->buffer_size);
+               /* just keep going */
+               return 0;
+       }
+
+       if (!atomic_read(&tmpbuf.datalen)) {
+               pr_debug("found erased zone: %s: off %lld, size %zu, datalen %d\n",
+                               zone->name, zone->off, zone->buffer_size,
+                               atomic_read(&tmpbuf.datalen));
+               return 0;
+       }
+
+       pr_debug("found nice zone: %s: off %lld, size %zu, datalen %d\n",
+                       zone->name, zone->off, zone->buffer_size,
+                       atomic_read(&tmpbuf.datalen));
+
+       len = atomic_read(&tmpbuf.datalen) + sizeof(*oldbuf);
+       oldbuf = kzalloc(len, GFP_KERNEL);
+       if (!oldbuf)
+               return -ENOMEM;
+
+       memcpy(oldbuf, &tmpbuf, sizeof(*oldbuf));
+       buf = (char *)oldbuf + sizeof(*oldbuf);
+       len = atomic_read(&oldbuf->datalen);
+       start = atomic_read(&oldbuf->start);
+       off = zone->off + sizeof(*oldbuf);
+
+       /* get part of data */
+       rcnt = info->read(buf, len - start, off + start);
+       if (rcnt != len - start) {
+               pr_err("read zone %s failed\n", zone->name);
+               ret = (int)rcnt < 0 ? (int)rcnt : -EIO;
+               goto free_oldbuf;
+       }
+
+       /* get the rest of data */
+       rcnt = info->read(buf + len - start, start, off);
+       if (rcnt != start) {
+               pr_err("read zone %s failed\n", zone->name);
+               ret = (int)rcnt < 0 ? (int)rcnt : -EIO;
+               goto free_oldbuf;
+       }
+
+       zone->oldbuf = oldbuf;
+       psz_flush_dirty_zone(zone);
+       return 0;
+
+free_oldbuf:
+       kfree(oldbuf);
+       return ret;
+}
+
+static int psz_recover_zones(struct psz_context *cxt,
+               struct pstore_zone **zones, unsigned int cnt)
+{
+       int ret;
+       unsigned int i;
+       struct pstore_zone *zone;
+
+       if (!zones)
+               return 0;
+
+       for (i = 0; i < cnt; i++) {
+               zone = zones[i];
+               if (unlikely(!zone))
+                       continue;
+               ret = psz_recover_zone(cxt, zone);
+               if (ret)
+                       goto recover_fail;
+       }
+
+       return 0;
+recover_fail:
+       pr_debug("recover %s[%u] failed\n", zone->name, i);
+       return ret;
+}
+
+/**
+ * psz_recovery() - recover data from storage
+ * @cxt: the context of pstore/zone
+ *
+ * recovery means reading data back from storage after rebooting
+ *
+ * Return: 0 on success, others on failure.
+ */
+static inline int psz_recovery(struct psz_context *cxt)
+{
+       int ret;
+
+       if (atomic_read(&cxt->recovered))
+               return 0;
+
+       ret = psz_kmsg_recover(cxt);
+       if (ret)
+               goto out;
+
+       ret = psz_recover_zone(cxt, cxt->ppsz);
+       if (ret)
+               goto out;
+
+       ret = psz_recover_zone(cxt, cxt->cpsz);
+       if (ret)
+               goto out;
+
+       ret = psz_recover_zones(cxt, cxt->fpszs, cxt->ftrace_max_cnt);
+
+out:
+       if (unlikely(ret))
+               pr_err("recover failed\n");
+       else {
+               pr_debug("recover end!\n");
+               atomic_set(&cxt->recovered, 1);
+       }
+       return ret;
+}
+
+static int psz_pstore_open(struct pstore_info *psi)
+{
+       struct psz_context *cxt = psi->data;
+
+       cxt->kmsg_read_cnt = 0;
+       cxt->pmsg_read_cnt = 0;
+       cxt->console_read_cnt = 0;
+       cxt->ftrace_read_cnt = 0;
+       return 0;
+}
+
+static inline bool psz_old_ok(struct pstore_zone *zone)
+{
+       if (zone && zone->oldbuf && atomic_read(&zone->oldbuf->datalen))
+               return true;
+       return false;
+}
+
+static inline bool psz_ok(struct pstore_zone *zone)
+{
+       if (zone && zone->buffer && buffer_datalen(zone))
+               return true;
+       return false;
+}
+
+static inline int psz_kmsg_erase(struct psz_context *cxt,
+               struct pstore_zone *zone, struct pstore_record *record)
+{
+       struct psz_buffer *buffer = zone->buffer;
+       struct psz_kmsg_header *hdr =
+               (struct psz_kmsg_header *)buffer->data;
+       size_t size;
+
+       if (unlikely(!psz_ok(zone)))
+               return 0;
+
+       /* this zone is already updated, no need to erase */
+       if (record->count != hdr->counter)
+               return 0;
+
+       size = buffer_datalen(zone) + sizeof(*zone->buffer);
+       atomic_set(&zone->buffer->datalen, 0);
+       if (cxt->pstore_zone_info->erase)
+               return cxt->pstore_zone_info->erase(size, zone->off);
+       else
+               return psz_zone_write(zone, FLUSH_META, NULL, 0, 0);
+}
+
+static inline int psz_record_erase(struct psz_context *cxt,
+               struct pstore_zone *zone)
+{
+       if (unlikely(!psz_old_ok(zone)))
+               return 0;
+
+       kfree(zone->oldbuf);
+       zone->oldbuf = NULL;
+       /*
+        * if there are new data in zone buffer, that means the old data
+        * are already invalid. It is no need to flush 0 (erase) to
+        * block device.
+        */
+       if (!buffer_datalen(zone))
+               return psz_zone_write(zone, FLUSH_META, NULL, 0, 0);
+       psz_flush_dirty_zone(zone);
+       return 0;
+}
+
+static int psz_pstore_erase(struct pstore_record *record)
+{
+       struct psz_context *cxt = record->psi->data;
+
+       switch (record->type) {
+       case PSTORE_TYPE_DMESG:
+               if (record->id >= cxt->kmsg_max_cnt)
+                       return -EINVAL;
+               return psz_kmsg_erase(cxt, cxt->kpszs[record->id], record);
+       case PSTORE_TYPE_PMSG:
+               return psz_record_erase(cxt, cxt->ppsz);
+       case PSTORE_TYPE_CONSOLE:
+               return psz_record_erase(cxt, cxt->cpsz);
+       case PSTORE_TYPE_FTRACE:
+               if (record->id >= cxt->ftrace_max_cnt)
+                       return -EINVAL;
+               return psz_record_erase(cxt, cxt->fpszs[record->id]);
+       default: return -EINVAL;
+       }
+}
+
+static void psz_write_kmsg_hdr(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       struct psz_context *cxt = record->psi->data;
+       struct psz_buffer *buffer = zone->buffer;
+       struct psz_kmsg_header *hdr =
+               (struct psz_kmsg_header *)buffer->data;
+
+       hdr->magic = PSTORE_KMSG_HEADER_MAGIC;
+       hdr->compressed = record->compressed;
+       hdr->time.tv_sec = record->time.tv_sec;
+       hdr->time.tv_nsec = record->time.tv_nsec;
+       hdr->reason = record->reason;
+       if (hdr->reason == KMSG_DUMP_OOPS)
+               hdr->counter = ++cxt->oops_counter;
+       else if (hdr->reason == KMSG_DUMP_PANIC)
+               hdr->counter = ++cxt->panic_counter;
+       else
+               hdr->counter = 0;
+}
+
+/*
+ * In case zone is broken, which may occur to MTD device, we try each zones,
+ * start at cxt->kmsg_write_cnt.
+ */
+static inline int notrace psz_kmsg_write_record(struct psz_context *cxt,
+               struct pstore_record *record)
+{
+       size_t size, hlen;
+       struct pstore_zone *zone;
+       unsigned int i;
+
+       for (i = 0; i < cxt->kmsg_max_cnt; i++) {
+               unsigned int zonenum, len;
+               int ret;
+
+               zonenum = (cxt->kmsg_write_cnt + i) % cxt->kmsg_max_cnt;
+               zone = cxt->kpszs[zonenum];
+               if (unlikely(!zone))
+                       return -ENOSPC;
+
+               /* avoid destroying old data, allocate a new one */
+               len = zone->buffer_size + sizeof(*zone->buffer);
+               zone->oldbuf = zone->buffer;
+               zone->buffer = kzalloc(len, GFP_KERNEL);
+               if (!zone->buffer) {
+                       zone->buffer = zone->oldbuf;
+                       return -ENOMEM;
+               }
+               zone->buffer->sig = zone->oldbuf->sig;
+
+               pr_debug("write %s to zone id %d\n", zone->name, zonenum);
+               psz_write_kmsg_hdr(zone, record);
+               hlen = sizeof(struct psz_kmsg_header);
+               size = min_t(size_t, record->size, zone->buffer_size - hlen);
+               ret = psz_zone_write(zone, FLUSH_ALL, record->buf, size, hlen);
+               if (likely(!ret || ret != -ENOMSG)) {
+                       cxt->kmsg_write_cnt = zonenum + 1;
+                       cxt->kmsg_write_cnt %= cxt->kmsg_max_cnt;
+                       /* no need to try next zone, free last zone buffer */
+                       kfree(zone->oldbuf);
+                       zone->oldbuf = NULL;
+                       return ret;
+               }
+
+               pr_debug("zone %u may be broken, try next dmesg zone\n",
+                               zonenum);
+               kfree(zone->buffer);
+               zone->buffer = zone->oldbuf;
+               zone->oldbuf = NULL;
+       }
+
+       return -EBUSY;
+}
+
+static int notrace psz_kmsg_write(struct psz_context *cxt,
+               struct pstore_record *record)
+{
+       int ret;
+
+       /*
+        * Explicitly only take the first part of any new crash.
+        * If our buffer is larger than kmsg_bytes, this can never happen,
+        * and if our buffer is smaller than kmsg_bytes, we don't want the
+        * report split across multiple records.
+        */
+       if (record->part != 1)
+               return -ENOSPC;
+
+       if (!cxt->kpszs)
+               return -ENOSPC;
+
+       ret = psz_kmsg_write_record(cxt, record);
+       if (!ret && is_on_panic()) {
+               /* ensure all data are flushed to storage when panic */
+               pr_debug("try to flush other dirty zones\n");
+               psz_flush_all_dirty_zones(NULL);
+       }
+
+       /* always return 0 as we had handled it on buffer */
+       return 0;
+}
+
+static int notrace psz_record_write(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       size_t start, rem;
+       bool is_full_data = false;
+       char *buf;
+       int cnt;
+
+       if (!zone || !record)
+               return -ENOSPC;
+
+       if (atomic_read(&zone->buffer->datalen) >= zone->buffer_size)
+               is_full_data = true;
+
+       cnt = record->size;
+       buf = record->buf;
+       if (unlikely(cnt > zone->buffer_size)) {
+               buf += cnt - zone->buffer_size;
+               cnt = zone->buffer_size;
+       }
+
+       start = buffer_start(zone);
+       rem = zone->buffer_size - start;
+       if (unlikely(rem < cnt)) {
+               psz_zone_write(zone, FLUSH_PART, buf, rem, start);
+               buf += rem;
+               cnt -= rem;
+               start = 0;
+               is_full_data = true;
+       }
+
+       atomic_set(&zone->buffer->start, cnt + start);
+       psz_zone_write(zone, FLUSH_PART, buf, cnt, start);
+
+       /**
+        * psz_zone_write will set datalen as start + cnt.
+        * It work if actual data length lesser than buffer size.
+        * If data length greater than buffer size, pmsg will rewrite to
+        * beginning of zone, which make buffer->datalen wrongly.
+        * So we should reset datalen as buffer size once actual data length
+        * greater than buffer size.
+        */
+       if (is_full_data) {
+               atomic_set(&zone->buffer->datalen, zone->buffer_size);
+               psz_zone_write(zone, FLUSH_META, NULL, 0, 0);
+       }
+       return 0;
+}
+
+static int notrace psz_pstore_write(struct pstore_record *record)
+{
+       struct psz_context *cxt = record->psi->data;
+
+       if (record->type == PSTORE_TYPE_DMESG &&
+                       record->reason == KMSG_DUMP_PANIC)
+               atomic_set(&cxt->on_panic, 1);
+
+       /*
+        * if on panic, do not write except panic records
+        * Fix case that panic_write prints log which wakes up console backend.
+        */
+       if (is_on_panic() && record->type != PSTORE_TYPE_DMESG)
+               return -EBUSY;
+
+       switch (record->type) {
+       case PSTORE_TYPE_DMESG:
+               return psz_kmsg_write(cxt, record);
+       case PSTORE_TYPE_CONSOLE:
+               return psz_record_write(cxt->cpsz, record);
+       case PSTORE_TYPE_PMSG:
+               return psz_record_write(cxt->ppsz, record);
+       case PSTORE_TYPE_FTRACE: {
+               int zonenum = smp_processor_id();
+
+               if (!cxt->fpszs)
+                       return -ENOSPC;
+               return psz_record_write(cxt->fpszs[zonenum], record);
+       }
+       default:
+               return -EINVAL;
+       }
+}
+
+static struct pstore_zone *psz_read_next_zone(struct psz_context *cxt)
+{
+       struct pstore_zone *zone = NULL;
+
+       while (cxt->kmsg_read_cnt < cxt->kmsg_max_cnt) {
+               zone = cxt->kpszs[cxt->kmsg_read_cnt++];
+               if (psz_ok(zone))
+                       return zone;
+       }
+
+       if (cxt->ftrace_read_cnt < cxt->ftrace_max_cnt)
+               /*
+                * No need psz_old_ok(). Let psz_ftrace_read() do so for
+                * combination. psz_ftrace_read() should traverse over
+                * all zones in case of some zone without data.
+                */
+               return cxt->fpszs[cxt->ftrace_read_cnt++];
+
+       if (cxt->pmsg_read_cnt == 0) {
+               cxt->pmsg_read_cnt++;
+               zone = cxt->ppsz;
+               if (psz_old_ok(zone))
+                       return zone;
+       }
+
+       if (cxt->console_read_cnt == 0) {
+               cxt->console_read_cnt++;
+               zone = cxt->cpsz;
+               if (psz_old_ok(zone))
+                       return zone;
+       }
+
+       return NULL;
+}
+
+static int psz_kmsg_read_hdr(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       struct psz_buffer *buffer = zone->buffer;
+       struct psz_kmsg_header *hdr =
+               (struct psz_kmsg_header *)buffer->data;
+
+       if (hdr->magic != PSTORE_KMSG_HEADER_MAGIC)
+               return -EINVAL;
+       record->compressed = hdr->compressed;
+       record->time.tv_sec = hdr->time.tv_sec;
+       record->time.tv_nsec = hdr->time.tv_nsec;
+       record->reason = hdr->reason;
+       record->count = hdr->counter;
+       return 0;
+}
+
+static ssize_t psz_kmsg_read(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       ssize_t size, hlen = 0;
+
+       size = buffer_datalen(zone);
+       /* Clear and skip this kmsg dump record if it has no valid header */
+       if (psz_kmsg_read_hdr(zone, record)) {
+               atomic_set(&zone->buffer->datalen, 0);
+               atomic_set(&zone->dirty, 0);
+               return -ENOMSG;
+       }
+       size -= sizeof(struct psz_kmsg_header);
+
+       if (!record->compressed) {
+               char *buf = kasprintf(GFP_KERNEL, "%s: Total %d times\n",
+                                     kmsg_dump_reason_str(record->reason),
+                                     record->count);
+               hlen = strlen(buf);
+               record->buf = krealloc(buf, hlen + size, GFP_KERNEL);
+               if (!record->buf) {
+                       kfree(buf);
+                       return -ENOMEM;
+               }
+       } else {
+               record->buf = kmalloc(size, GFP_KERNEL);
+               if (!record->buf)
+                       return -ENOMEM;
+       }
+
+       size = psz_zone_read_buffer(zone, record->buf + hlen, size,
+                       sizeof(struct psz_kmsg_header));
+       if (unlikely(size < 0)) {
+               kfree(record->buf);
+               return -ENOMSG;
+       }
+
+       return size + hlen;
+}
+
+/* try to combine all ftrace zones */
+static ssize_t psz_ftrace_read(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       struct psz_context *cxt;
+       struct psz_buffer *buf;
+       int ret;
+
+       if (!zone || !record)
+               return -ENOSPC;
+
+       if (!psz_old_ok(zone))
+               goto out;
+
+       buf = (struct psz_buffer *)zone->oldbuf;
+       if (!buf)
+               return -ENOMSG;
+
+       ret = pstore_ftrace_combine_log(&record->buf, &record->size,
+                       (char *)buf->data, atomic_read(&buf->datalen));
+       if (unlikely(ret))
+               return ret;
+
+out:
+       cxt = record->psi->data;
+       if (cxt->ftrace_read_cnt < cxt->ftrace_max_cnt)
+               /* then, read next ftrace zone */
+               return -ENOMSG;
+       record->id = 0;
+       return record->size ? record->size : -ENOMSG;
+}
+
+static ssize_t psz_record_read(struct pstore_zone *zone,
+               struct pstore_record *record)
+{
+       size_t len;
+       struct psz_buffer *buf;
+
+       if (!zone || !record)
+               return -ENOSPC;
+
+       buf = (struct psz_buffer *)zone->oldbuf;
+       if (!buf)
+               return -ENOMSG;
+
+       len = atomic_read(&buf->datalen);
+       record->buf = kmalloc(len, GFP_KERNEL);
+       if (!record->buf)
+               return -ENOMEM;
+
+       if (unlikely(psz_zone_read_oldbuf(zone, record->buf, len, 0))) {
+               kfree(record->buf);
+               return -ENOMSG;
+       }
+
+       return len;
+}
+
+static ssize_t psz_pstore_read(struct pstore_record *record)
+{
+       struct psz_context *cxt = record->psi->data;
+       ssize_t (*readop)(struct pstore_zone *zone,
+                       struct pstore_record *record);
+       struct pstore_zone *zone;
+       ssize_t ret;
+
+       /* before read, we must recover from storage */
+       ret = psz_recovery(cxt);
+       if (ret)
+               return ret;
+
+next_zone:
+       zone = psz_read_next_zone(cxt);
+       if (!zone)
+               return 0;
+
+       record->type = zone->type;
+       switch (record->type) {
+       case PSTORE_TYPE_DMESG:
+               readop = psz_kmsg_read;
+               record->id = cxt->kmsg_read_cnt - 1;
+               break;
+       case PSTORE_TYPE_FTRACE:
+               readop = psz_ftrace_read;
+               break;
+       case PSTORE_TYPE_CONSOLE:
+               fallthrough;
+       case PSTORE_TYPE_PMSG:
+               readop = psz_record_read;
+               break;
+       default:
+               goto next_zone;
+       }
+
+       ret = readop(zone, record);
+       if (ret == -ENOMSG)
+               goto next_zone;
+       return ret;
+}
+
+static struct psz_context pstore_zone_cxt = {
+       .pstore_zone_info_lock =
+               __MUTEX_INITIALIZER(pstore_zone_cxt.pstore_zone_info_lock),
+       .recovered = ATOMIC_INIT(0),
+       .on_panic = ATOMIC_INIT(0),
+       .pstore = {
+               .owner = THIS_MODULE,
+               .open = psz_pstore_open,
+               .read = psz_pstore_read,
+               .write = psz_pstore_write,
+               .erase = psz_pstore_erase,
+       },
+};
+
+static void psz_free_zone(struct pstore_zone **pszone)
+{
+       struct pstore_zone *zone = *pszone;
+
+       if (!zone)
+               return;
+
+       kfree(zone->buffer);
+       kfree(zone);
+       *pszone = NULL;
+}
+
+static void psz_free_zones(struct pstore_zone ***pszones, unsigned int *cnt)
+{
+       struct pstore_zone **zones = *pszones;
+
+       if (!zones)
+               return;
+
+       while (*cnt > 0) {
+               (*cnt)--;
+               psz_free_zone(&(zones[*cnt]));
+       }
+       kfree(zones);
+       *pszones = NULL;
+}
+
+static void psz_free_all_zones(struct psz_context *cxt)
+{
+       if (cxt->kpszs)
+               psz_free_zones(&cxt->kpszs, &cxt->kmsg_max_cnt);
+       if (cxt->ppsz)
+               psz_free_zone(&cxt->ppsz);
+       if (cxt->cpsz)
+               psz_free_zone(&cxt->cpsz);
+       if (cxt->fpszs)
+               psz_free_zones(&cxt->fpszs, &cxt->ftrace_max_cnt);
+}
+
+static struct pstore_zone *psz_init_zone(enum pstore_type_id type,
+               loff_t *off, size_t size)
+{
+       struct pstore_zone_info *info = pstore_zone_cxt.pstore_zone_info;
+       struct pstore_zone *zone;
+       const char *name = pstore_type_to_name(type);
+
+       if (!size)
+               return NULL;
+
+       if (*off + size > info->total_size) {
+               pr_err("no room for %s (0x%zx@0x%llx over 0x%lx)\n",
+                       name, size, *off, info->total_size);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       zone = kzalloc(sizeof(struct pstore_zone), GFP_KERNEL);
+       if (!zone)
+               return ERR_PTR(-ENOMEM);
+
+       zone->buffer = kmalloc(size, GFP_KERNEL);
+       if (!zone->buffer) {
+               kfree(zone);
+               return ERR_PTR(-ENOMEM);
+       }
+       memset(zone->buffer, 0xFF, size);
+       zone->off = *off;
+       zone->name = name;
+       zone->type = type;
+       zone->buffer_size = size - sizeof(struct psz_buffer);
+       zone->buffer->sig = type ^ PSZ_SIG;
+       zone->oldbuf = NULL;
+       atomic_set(&zone->dirty, 0);
+       atomic_set(&zone->buffer->datalen, 0);
+       atomic_set(&zone->buffer->start, 0);
+
+       *off += size;
+
+       pr_debug("pszone %s: off 0x%llx, %zu header, %zu data\n", zone->name,
+                       zone->off, sizeof(*zone->buffer), zone->buffer_size);
+       return zone;
+}
+
+static struct pstore_zone **psz_init_zones(enum pstore_type_id type,
+       loff_t *off, size_t total_size, ssize_t record_size,
+       unsigned int *cnt)
+{
+       struct pstore_zone_info *info = pstore_zone_cxt.pstore_zone_info;
+       struct pstore_zone **zones, *zone;
+       const char *name = pstore_type_to_name(type);
+       int c, i;
+
+       *cnt = 0;
+       if (!total_size || !record_size)
+               return NULL;
+
+       if (*off + total_size > info->total_size) {
+               pr_err("no room for zones %s (0x%zx@0x%llx over 0x%lx)\n",
+                       name, total_size, *off, info->total_size);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       c = total_size / record_size;
+       zones = kcalloc(c, sizeof(*zones), GFP_KERNEL);
+       if (!zones) {
+               pr_err("allocate for zones %s failed\n", name);
+               return ERR_PTR(-ENOMEM);
+       }
+       memset(zones, 0, c * sizeof(*zones));
+
+       for (i = 0; i < c; i++) {
+               zone = psz_init_zone(type, off, record_size);
+               if (!zone || IS_ERR(zone)) {
+                       pr_err("initialize zones %s failed\n", name);
+                       psz_free_zones(&zones, &i);
+                       return (void *)zone;
+               }
+               zones[i] = zone;
+       }
+
+       *cnt = c;
+       return zones;
+}
+
+static int psz_alloc_zones(struct psz_context *cxt)
+{
+       struct pstore_zone_info *info = cxt->pstore_zone_info;
+       loff_t off = 0;
+       int err;
+       size_t off_size = 0;
+
+       off_size += info->pmsg_size;
+       cxt->ppsz = psz_init_zone(PSTORE_TYPE_PMSG, &off, info->pmsg_size);
+       if (IS_ERR(cxt->ppsz)) {
+               err = PTR_ERR(cxt->ppsz);
+               cxt->ppsz = NULL;
+               goto free_out;
+       }
+
+       off_size += info->console_size;
+       cxt->cpsz = psz_init_zone(PSTORE_TYPE_CONSOLE, &off,
+                       info->console_size);
+       if (IS_ERR(cxt->cpsz)) {
+               err = PTR_ERR(cxt->cpsz);
+               cxt->cpsz = NULL;
+               goto free_out;
+       }
+
+       off_size += info->ftrace_size;
+       cxt->fpszs = psz_init_zones(PSTORE_TYPE_FTRACE, &off,
+                       info->ftrace_size,
+                       info->ftrace_size / nr_cpu_ids,
+                       &cxt->ftrace_max_cnt);
+       if (IS_ERR(cxt->fpszs)) {
+               err = PTR_ERR(cxt->fpszs);
+               cxt->fpszs = NULL;
+               goto free_out;
+       }
+
+       cxt->kpszs = psz_init_zones(PSTORE_TYPE_DMESG, &off,
+                       info->total_size - off_size,
+                       info->kmsg_size, &cxt->kmsg_max_cnt);
+       if (IS_ERR(cxt->kpszs)) {
+               err = PTR_ERR(cxt->kpszs);
+               cxt->kpszs = NULL;
+               goto free_out;
+       }
+
+       return 0;
+free_out:
+       psz_free_all_zones(cxt);
+       return err;
+}
+
+/**
+ * register_pstore_zone() - register to pstore/zone
+ *
+ * @info: back-end driver information. See &struct pstore_zone_info.
+ *
+ * Only one back-end at one time.
+ *
+ * Return: 0 on success, others on failure.
+ */
+int register_pstore_zone(struct pstore_zone_info *info)
+{
+       int err = -EINVAL;
+       struct psz_context *cxt = &pstore_zone_cxt;
+
+       if (info->total_size < 4096) {
+               pr_warn("total_size must be >= 4096\n");
+               return -EINVAL;
+       }
+
+       if (!info->kmsg_size && !info->pmsg_size && !info->console_size &&
+           !info->ftrace_size) {
+               pr_warn("at least one record size must be non-zero\n");
+               return -EINVAL;
+       }
+
+       if (!info->name || !info->name[0])
+               return -EINVAL;
+
+#define check_size(name, size) {                                       \
+               if (info->name > 0 && info->name < (size)) {            \
+                       pr_err(#name " must be over %d\n", (size));     \
+                       return -EINVAL;                                 \
+               }                                                       \
+               if (info->name & (size - 1)) {                          \
+                       pr_err(#name " must be a multiple of %d\n",     \
+                                       (size));                        \
+                       return -EINVAL;                                 \
+               }                                                       \
+       }
+
+       check_size(total_size, 4096);
+       check_size(kmsg_size, SECTOR_SIZE);
+       check_size(pmsg_size, SECTOR_SIZE);
+       check_size(console_size, SECTOR_SIZE);
+       check_size(ftrace_size, SECTOR_SIZE);
+
+#undef check_size
+
+       /*
+        * the @read and @write must be applied.
+        * if no @read, pstore may mount failed.
+        * if no @write, pstore do not support to remove record file.
+        */
+       if (!info->read || !info->write) {
+               pr_err("no valid general read/write interface\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&cxt->pstore_zone_info_lock);
+       if (cxt->pstore_zone_info) {
+               pr_warn("'%s' already loaded: ignoring '%s'\n",
+                               cxt->pstore_zone_info->name, info->name);
+               mutex_unlock(&cxt->pstore_zone_info_lock);
+               return -EBUSY;
+       }
+       cxt->pstore_zone_info = info;
+
+       pr_debug("register %s with properties:\n", info->name);
+       pr_debug("\ttotal size : %ld Bytes\n", info->total_size);
+       pr_debug("\tkmsg size : %ld Bytes\n", info->kmsg_size);
+       pr_debug("\tpmsg size : %ld Bytes\n", info->pmsg_size);
+       pr_debug("\tconsole size : %ld Bytes\n", info->console_size);
+       pr_debug("\tftrace size : %ld Bytes\n", info->ftrace_size);
+
+       err = psz_alloc_zones(cxt);
+       if (err) {
+               pr_err("alloc zones failed\n");
+               goto fail_out;
+       }
+
+       if (info->kmsg_size) {
+               cxt->pstore.bufsize = cxt->kpszs[0]->buffer_size -
+                       sizeof(struct psz_kmsg_header);
+               cxt->pstore.buf = kzalloc(cxt->pstore.bufsize, GFP_KERNEL);
+               if (!cxt->pstore.buf) {
+                       err = -ENOMEM;
+                       goto fail_free;
+               }
+       }
+       cxt->pstore.data = cxt;
+
+       pr_info("registered %s as backend for", info->name);
+       cxt->pstore.max_reason = info->max_reason;
+       cxt->pstore.name = info->name;
+       if (info->kmsg_size) {
+               cxt->pstore.flags |= PSTORE_FLAGS_DMESG;
+               pr_cont(" kmsg(%s",
+                       kmsg_dump_reason_str(cxt->pstore.max_reason));
+               if (cxt->pstore_zone_info->panic_write)
+                       pr_cont(",panic_write");
+               pr_cont(")");
+       }
+       if (info->pmsg_size) {
+               cxt->pstore.flags |= PSTORE_FLAGS_PMSG;
+               pr_cont(" pmsg");
+       }
+       if (info->console_size) {
+               cxt->pstore.flags |= PSTORE_FLAGS_CONSOLE;
+               pr_cont(" console");
+       }
+       if (info->ftrace_size) {
+               cxt->pstore.flags |= PSTORE_FLAGS_FTRACE;
+               pr_cont(" ftrace");
+       }
+       pr_cont("\n");
+
+       err = pstore_register(&cxt->pstore);
+       if (err) {
+               pr_err("registering with pstore failed\n");
+               goto fail_free;
+       }
+       mutex_unlock(&pstore_zone_cxt.pstore_zone_info_lock);
+
+       return 0;
+
+fail_free:
+       kfree(cxt->pstore.buf);
+       cxt->pstore.buf = NULL;
+       cxt->pstore.bufsize = 0;
+       psz_free_all_zones(cxt);
+fail_out:
+       pstore_zone_cxt.pstore_zone_info = NULL;
+       mutex_unlock(&pstore_zone_cxt.pstore_zone_info_lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(register_pstore_zone);
+
+/**
+ * unregister_pstore_zone() - unregister to pstore/zone
+ *
+ * @info: back-end driver information. See struct pstore_zone_info.
+ */
+void unregister_pstore_zone(struct pstore_zone_info *info)
+{
+       struct psz_context *cxt = &pstore_zone_cxt;
+
+       mutex_lock(&cxt->pstore_zone_info_lock);
+       if (!cxt->pstore_zone_info) {
+               mutex_unlock(&cxt->pstore_zone_info_lock);
+               return;
+       }
+
+       /* Stop incoming writes from pstore. */
+       pstore_unregister(&cxt->pstore);
+
+       /* Flush any pending writes. */
+       psz_flush_all_dirty_zones(NULL);
+       flush_delayed_work(&psz_cleaner);
+
+       /* Clean up allocations. */
+       kfree(cxt->pstore.buf);
+       cxt->pstore.buf = NULL;
+       cxt->pstore.bufsize = 0;
+       cxt->pstore_zone_info = NULL;
+
+       psz_free_all_zones(cxt);
+
+       /* Clear counters and zone state. */
+       cxt->oops_counter = 0;
+       cxt->panic_counter = 0;
+       atomic_set(&cxt->recovered, 0);
+       atomic_set(&cxt->on_panic, 0);
+
+       mutex_unlock(&cxt->pstore_zone_info_lock);
+}
+EXPORT_SYMBOL_GPL(unregister_pstore_zone);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");
+MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("Storage Manager for pstore/blk");
index 345db56c98fd7dbf31e3be56bc96c1ac66d8e849..755293c8c71a631e5afcc81551072cb5d8acbf85 100644 (file)
@@ -99,10 +99,9 @@ static int qnx6_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, qnx6_get_block);
 }
 
-static int qnx6_readpages(struct file *file, struct address_space *mapping,
-                  struct list_head *pages, unsigned nr_pages)
+static void qnx6_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, qnx6_get_block);
+       mpage_readahead(rac, qnx6_get_block);
 }
 
 /*
@@ -499,7 +498,7 @@ static sector_t qnx6_bmap(struct address_space *mapping, sector_t block)
 }
 static const struct address_space_operations qnx6_aops = {
        .readpage       = qnx6_readpage,
-       .readpages      = qnx6_readpages,
+       .readahead      = qnx6_readahead,
        .bmap           = qnx6_bmap
 };
 
index de2eceffdee8baf97f584bf644872822fdef8ef3..a49f07c11cfbd09ca448ac38cb9febe0d169a143 100644 (file)
@@ -157,17 +157,18 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
        }
        buf->result++;
        dirent = buf->dirent;
-       if (!access_ok(dirent,
+       if (!user_write_access_begin(dirent,
                        (unsigned long)(dirent->d_name + namlen + 1) -
                                (unsigned long)dirent))
                goto efault;
-       if (    __put_user(d_ino, &dirent->d_ino) ||
-               __put_user(offset, &dirent->d_offset) ||
-               __put_user(namlen, &dirent->d_namlen) ||
-               __copy_to_user(dirent->d_name, name, namlen) ||
-               __put_user(0, dirent->d_name + namlen))
-               goto efault;
+       unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
+       unsafe_put_user(offset, &dirent->d_offset, efault_end);
+       unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
+       unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
+       user_write_access_end();
        return 0;
+efault_end:
+       user_write_access_end();
 efault:
        buf->result = -EFAULT;
        return -EFAULT;
@@ -242,7 +243,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
                return -EINTR;
        dirent = buf->current_dir;
        prev = (void __user *) dirent - prev_reclen;
-       if (!user_access_begin(prev, reclen + prev_reclen))
+       if (!user_write_access_begin(prev, reclen + prev_reclen))
                goto efault;
 
        /* This might be 'dirent->d_off', but if so it will get overwritten */
@@ -251,14 +252,14 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
        unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
        unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
        unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
-       user_access_end();
+       user_write_access_end();
 
        buf->current_dir = (void __user *)dirent + reclen;
        buf->prev_reclen = reclen;
        buf->count -= reclen;
        return 0;
 efault_end:
-       user_access_end();
+       user_write_access_end();
 efault:
        buf->error = -EFAULT;
        return -EFAULT;
@@ -275,9 +276,6 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
        };
        int error;
 
-       if (!access_ok(dirent, count))
-               return -EFAULT;
-
        f = fdget_pos(fd);
        if (!f.file)
                return -EBADF;
@@ -327,7 +325,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
                return -EINTR;
        dirent = buf->current_dir;
        prev = (void __user *)dirent - prev_reclen;
-       if (!user_access_begin(prev, reclen + prev_reclen))
+       if (!user_write_access_begin(prev, reclen + prev_reclen))
                goto efault;
 
        /* This might be 'dirent->d_off', but if so it will get overwritten */
@@ -336,7 +334,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
        unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
        unsafe_put_user(d_type, &dirent->d_type, efault_end);
        unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
-       user_access_end();
+       user_write_access_end();
 
        buf->prev_reclen = reclen;
        buf->current_dir = (void __user *)dirent + reclen;
@@ -344,7 +342,7 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
        return 0;
 
 efault_end:
-       user_access_end();
+       user_write_access_end();
 efault:
        buf->error = -EFAULT;
        return -EFAULT;
@@ -361,9 +359,6 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
        };
        int error;
 
-       if (!access_ok(dirent, count))
-               return -EFAULT;
-
        f = fdget_pos(fd);
        if (!f.file)
                return -EBADF;
@@ -376,7 +371,7 @@ int ksys_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
                typeof(lastdirent->d_off) d_off = buf.ctx.pos;
 
                lastdirent = (void __user *) buf.current_dir - buf.prev_reclen;
-               if (__put_user(d_off, &lastdirent->d_off))
+               if (put_user(d_off, &lastdirent->d_off))
                        error = -EFAULT;
                else
                        error = count - buf.count;
@@ -424,17 +419,18 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name,
        }
        buf->result++;
        dirent = buf->dirent;
-       if (!access_ok(dirent,
+       if (!user_write_access_begin(dirent,
                        (unsigned long)(dirent->d_name + namlen + 1) -
                                (unsigned long)dirent))
                goto efault;
-       if (    __put_user(d_ino, &dirent->d_ino) ||
-               __put_user(offset, &dirent->d_offset) ||
-               __put_user(namlen, &dirent->d_namlen) ||
-               __copy_to_user(dirent->d_name, name, namlen) ||
-               __put_user(0, dirent->d_name + namlen))
-               goto efault;
+       unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
+       unsafe_put_user(offset, &dirent->d_offset, efault_end);
+       unsafe_put_user(namlen, &dirent->d_namlen, efault_end);
+       unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
+       user_write_access_end();
        return 0;
+efault_end:
+       user_write_access_end();
 efault:
        buf->result = -EFAULT;
        return -EFAULT;
@@ -471,7 +467,7 @@ struct compat_linux_dirent {
 struct compat_getdents_callback {
        struct dir_context ctx;
        struct compat_linux_dirent __user *current_dir;
-       struct compat_linux_dirent __user *previous;
+       int prev_reclen;
        int count;
        int error;
 };
@@ -479,13 +475,17 @@ struct compat_getdents_callback {
 static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
                loff_t offset, u64 ino, unsigned int d_type)
 {
-       struct compat_linux_dirent __user * dirent;
+       struct compat_linux_dirent __user *dirent, *prev;
        struct compat_getdents_callback *buf =
                container_of(ctx, struct compat_getdents_callback, ctx);
        compat_ulong_t d_ino;
        int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
                namlen + 2, sizeof(compat_long_t));
+       int prev_reclen;
 
+       buf->error = verify_dirent_name(name, namlen);
+       if (unlikely(buf->error))
+               return buf->error;
        buf->error = -EINVAL;   /* only used if we fail.. */
        if (reclen > buf->count)
                return -EINVAL;
@@ -494,29 +494,27 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
                buf->error = -EOVERFLOW;
                return -EOVERFLOW;
        }
-       dirent = buf->previous;
-       if (dirent) {
-               if (signal_pending(current))
-                       return -EINTR;
-               if (__put_user(offset, &dirent->d_off))
-                       goto efault;
-       }
+       prev_reclen = buf->prev_reclen;
+       if (prev_reclen && signal_pending(current))
+               return -EINTR;
        dirent = buf->current_dir;
-       if (__put_user(d_ino, &dirent->d_ino))
-               goto efault;
-       if (__put_user(reclen, &dirent->d_reclen))
-               goto efault;
-       if (copy_to_user(dirent->d_name, name, namlen))
-               goto efault;
-       if (__put_user(0, dirent->d_name + namlen))
-               goto efault;
-       if (__put_user(d_type, (char  __user *) dirent + reclen - 1))
+       prev = (void __user *) dirent - prev_reclen;
+       if (!user_write_access_begin(prev, reclen + prev_reclen))
                goto efault;
-       buf->previous = dirent;
-       dirent = (void __user *)dirent + reclen;
-       buf->current_dir = dirent;
+
+       unsafe_put_user(offset, &prev->d_off, efault_end);
+       unsafe_put_user(d_ino, &dirent->d_ino, efault_end);
+       unsafe_put_user(reclen, &dirent->d_reclen, efault_end);
+       unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end);
+       unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end);
+       user_write_access_end();
+
+       buf->prev_reclen = reclen;
+       buf->current_dir = (void __user *)dirent + reclen;
        buf->count -= reclen;
        return 0;
+efault_end:
+       user_write_access_end();
 efault:
        buf->error = -EFAULT;
        return -EFAULT;
@@ -526,7 +524,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
                struct compat_linux_dirent __user *, dirent, unsigned int, count)
 {
        struct fd f;
-       struct compat_linux_dirent __user * lastdirent;
        struct compat_getdents_callback buf = {
                .ctx.actor = compat_filldir,
                .current_dir = dirent,
@@ -534,9 +531,6 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
        };
        int error;
 
-       if (!access_ok(dirent, count))
-               return -EFAULT;
-
        f = fdget_pos(fd);
        if (!f.file)
                return -EBADF;
@@ -544,8 +538,10 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
        error = iterate_dir(f.file, &buf.ctx);
        if (error >= 0)
                error = buf.error;
-       lastdirent = buf.previous;
-       if (lastdirent) {
+       if (buf.prev_reclen) {
+               struct compat_linux_dirent __user * lastdirent;
+               lastdirent = (void __user *)buf.current_dir - buf.prev_reclen;
+
                if (put_user(buf.ctx.pos, &lastdirent->d_off))
                        error = -EFAULT;
                else
index 6419e6dacc394dd0e3290a4fefe6af35b06039b2..0031070b3692cd8246ee6f44a379b7bf69e07984 100644 (file)
@@ -1160,11 +1160,9 @@ failure:
        return retval;
 }
 
-static int
-reiserfs_readpages(struct file *file, struct address_space *mapping,
-                  struct list_head *pages, unsigned nr_pages)
+static void reiserfs_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, reiserfs_get_block);
+       mpage_readahead(rac, reiserfs_get_block);
 }
 
 /*
@@ -3434,7 +3432,7 @@ out:
 const struct address_space_operations reiserfs_address_space_operations = {
        .writepage = reiserfs_writepage,
        .readpage = reiserfs_readpage,
-       .readpages = reiserfs_readpages,
+       .readahead = reiserfs_readahead,
        .releasepage = reiserfs_releasepage,
        .invalidatepage = reiserfs_invalidatepage,
        .write_begin = reiserfs_write_begin,
index ad4c457888961c0c711e7d457ff11afeb12b6ec5..9737b8e688781e2d5aadd73f7d08ff4feaff3eec 100644 (file)
@@ -6,7 +6,7 @@ config ROMFS_FS
          This is a very small read-only file system mainly intended for
          initial ram disks of installation disks, but it could be used for
          other read-only media as well.  Read
-         <file:Documentation/filesystems/romfs.txt> for details.
+         <file:Documentation/filesystems/romfs.rst> for details.
 
          To compile this file system support as a module, choose M here: the
          module will be called romfs.  Note that the file system of your
index 4f9b9fb593620442ab265c16b8518f5eef49f6d4..64f61330564acdb9b38995cfb8f02f2c040435e7 100644 (file)
@@ -13,6 +13,7 @@
  * datablocks and metadata blocks.
  */
 
+#include <linux/blkdev.h>
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/slab.h>
 #include "page_actor.h"
 
 /*
- * Read the metadata block length, this is stored in the first two
- * bytes of the metadata block.
+ * Returns the amount of bytes copied to the page actor.
  */
-static struct buffer_head *get_block_length(struct super_block *sb,
-                       u64 *cur_index, int *offset, int *length)
+static int copy_bio_to_actor(struct bio *bio,
+                            struct squashfs_page_actor *actor,
+                            int offset, int req_length)
+{
+       void *actor_addr = squashfs_first_page(actor);
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
+       int copied_bytes = 0;
+       int actor_offset = 0;
+
+       if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all)))
+               return 0;
+
+       while (copied_bytes < req_length) {
+               int bytes_to_copy = min_t(int, bvec->bv_len - offset,
+                                         PAGE_SIZE - actor_offset);
+
+               bytes_to_copy = min_t(int, bytes_to_copy,
+                                     req_length - copied_bytes);
+               memcpy(actor_addr + actor_offset,
+                      page_address(bvec->bv_page) + bvec->bv_offset + offset,
+                      bytes_to_copy);
+
+               actor_offset += bytes_to_copy;
+               copied_bytes += bytes_to_copy;
+               offset += bytes_to_copy;
+
+               if (actor_offset >= PAGE_SIZE) {
+                       actor_addr = squashfs_next_page(actor);
+                       if (!actor_addr)
+                               break;
+                       actor_offset = 0;
+               }
+               if (offset >= bvec->bv_len) {
+                       if (!bio_next_segment(bio, &iter_all))
+                               break;
+                       offset = 0;
+               }
+       }
+       squashfs_finish_page(actor);
+       return copied_bytes;
+}
+
+static int squashfs_bio_read(struct super_block *sb, u64 index, int length,
+                            struct bio **biop, int *block_offset)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
-       struct buffer_head *bh;
-
-       bh = sb_bread(sb, *cur_index);
-       if (bh == NULL)
-               return NULL;
-
-       if (msblk->devblksize - *offset == 1) {
-               *length = (unsigned char) bh->b_data[*offset];
-               put_bh(bh);
-               bh = sb_bread(sb, ++(*cur_index));
-               if (bh == NULL)
-                       return NULL;
-               *length |= (unsigned char) bh->b_data[0] << 8;
-               *offset = 1;
-       } else {
-               *length = (unsigned char) bh->b_data[*offset] |
-                       (unsigned char) bh->b_data[*offset + 1] << 8;
-               *offset += 2;
-
-               if (*offset == msblk->devblksize) {
-                       put_bh(bh);
-                       bh = sb_bread(sb, ++(*cur_index));
-                       if (bh == NULL)
-                               return NULL;
-                       *offset = 0;
+       const u64 read_start = round_down(index, msblk->devblksize);
+       const sector_t block = read_start >> msblk->devblksize_log2;
+       const u64 read_end = round_up(index + length, msblk->devblksize);
+       const sector_t block_end = read_end >> msblk->devblksize_log2;
+       int offset = read_start - round_down(index, PAGE_SIZE);
+       int total_len = (block_end - block) << msblk->devblksize_log2;
+       const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE);
+       int error, i;
+       struct bio *bio;
+
+       bio = bio_alloc(GFP_NOIO, page_count);
+       if (!bio)
+               return -ENOMEM;
+
+       bio_set_dev(bio, sb->s_bdev);
+       bio->bi_opf = READ;
+       bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT);
+
+       for (i = 0; i < page_count; ++i) {
+               unsigned int len =
+                       min_t(unsigned int, PAGE_SIZE - offset, total_len);
+               struct page *page = alloc_page(GFP_NOIO);
+
+               if (!page) {
+                       error = -ENOMEM;
+                       goto out_free_bio;
+               }
+               if (!bio_add_page(bio, page, len, offset)) {
+                       error = -EIO;
+                       goto out_free_bio;
                }
+               offset = 0;
+               total_len -= len;
        }
 
-       return bh;
-}
+       error = submit_bio_wait(bio);
+       if (error)
+               goto out_free_bio;
 
+       *biop = bio;
+       *block_offset = index & ((1 << msblk->devblksize_log2) - 1);
+       return 0;
+
+out_free_bio:
+       bio_free_pages(bio);
+       bio_put(bio);
+       return error;
+}
 
 /*
  * Read and decompress a metadata block or datablock.  Length is non-zero
@@ -76,129 +136,88 @@ static struct buffer_head *get_block_length(struct super_block *sb,
  * algorithms).
  */
 int squashfs_read_data(struct super_block *sb, u64 index, int length,
-               u64 *next_index, struct squashfs_page_actor *output)
+                      u64 *next_index, struct squashfs_page_actor *output)
 {
        struct squashfs_sb_info *msblk = sb->s_fs_info;
-       struct buffer_head **bh;
-       int offset = index & ((1 << msblk->devblksize_log2) - 1);
-       u64 cur_index = index >> msblk->devblksize_log2;
-       int bytes, compressed, b = 0, k = 0, avail, i;
-
-       bh = kcalloc(((output->length + msblk->devblksize - 1)
-               >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
-       if (bh == NULL)
-               return -ENOMEM;
+       struct bio *bio = NULL;
+       int compressed;
+       int res;
+       int offset;
 
        if (length) {
                /*
                 * Datablock.
                 */
-               bytes = -offset;
                compressed = SQUASHFS_COMPRESSED_BLOCK(length);
                length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
-               if (next_index)
-                       *next_index = index + length;
-
                TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
                        index, compressed ? "" : "un", length, output->length);
-
-               if (length < 0 || length > output->length ||
-                               (index + length) > msblk->bytes_used)
-                       goto read_failure;
-
-               for (b = 0; bytes < length; b++, cur_index++) {
-                       bh[b] = sb_getblk(sb, cur_index);
-                       if (bh[b] == NULL)
-                               goto block_release;
-                       bytes += msblk->devblksize;
-               }
-               ll_rw_block(REQ_OP_READ, 0, b, bh);
        } else {
                /*
                 * Metadata block.
                 */
-               if ((index + 2) > msblk->bytes_used)
-                       goto read_failure;
+               const u8 *data;
+               struct bvec_iter_all iter_all = {};
+               struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
 
-               bh[0] = get_block_length(sb, &cur_index, &offset, &length);
-               if (bh[0] == NULL)
-                       goto read_failure;
-               b = 1;
+               if (index + 2 > msblk->bytes_used) {
+                       res = -EIO;
+                       goto out;
+               }
+               res = squashfs_bio_read(sb, index, 2, &bio, &offset);
+               if (res)
+                       goto out;
+
+               if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
+                       res = -EIO;
+                       goto out_free_bio;
+               }
+               /* Extract the length of the metadata block */
+               data = page_address(bvec->bv_page) + bvec->bv_offset;
+               length = data[offset];
+               if (offset <= bvec->bv_len - 1) {
+                       length |= data[offset + 1] << 8;
+               } else {
+                       if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) {
+                               res = -EIO;
+                               goto out_free_bio;
+                       }
+                       data = page_address(bvec->bv_page) + bvec->bv_offset;
+                       length |= data[0] << 8;
+               }
+               bio_free_pages(bio);
+               bio_put(bio);
 
-               bytes = msblk->devblksize - offset;
                compressed = SQUASHFS_COMPRESSED(length);
                length = SQUASHFS_COMPRESSED_SIZE(length);
-               if (next_index)
-                       *next_index = index + length + 2;
+               index += 2;
 
                TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
-                               compressed ? "" : "un", length);
-
-               if (length < 0 || length > output->length ||
-                                       (index + length) > msblk->bytes_used)
-                       goto block_release;
-
-               for (; bytes < length; b++) {
-                       bh[b] = sb_getblk(sb, ++cur_index);
-                       if (bh[b] == NULL)
-                               goto block_release;
-                       bytes += msblk->devblksize;
-               }
-               ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
+                     compressed ? "" : "un", length);
        }
+       if (next_index)
+               *next_index = index + length;
 
-       for (i = 0; i < b; i++) {
-               wait_on_buffer(bh[i]);
-               if (!buffer_uptodate(bh[i]))
-                       goto block_release;
-       }
+       res = squashfs_bio_read(sb, index, length, &bio, &offset);
+       if (res)
+               goto out;
 
        if (compressed) {
-               if (!msblk->stream)
-                       goto read_failure;
-               length = squashfs_decompress(msblk, bh, b, offset, length,
-                       output);
-               if (length < 0)
-                       goto read_failure;
-       } else {
-               /*
-                * Block is uncompressed.
-                */
-               int in, pg_offset = 0;
-               void *data = squashfs_first_page(output);
-
-               for (bytes = length; k < b; k++) {
-                       in = min(bytes, msblk->devblksize - offset);
-                       bytes -= in;
-                       while (in) {
-                               if (pg_offset == PAGE_SIZE) {
-                                       data = squashfs_next_page(output);
-                                       pg_offset = 0;
-                               }
-                               avail = min_t(int, in, PAGE_SIZE -
-                                               pg_offset);
-                               memcpy(data + pg_offset, bh[k]->b_data + offset,
-                                               avail);
-                               in -= avail;
-                               pg_offset += avail;
-                               offset += avail;
-                       }
-                       offset = 0;
-                       put_bh(bh[k]);
+               if (!msblk->stream) {
+                       res = -EIO;
+                       goto out_free_bio;
                }
-               squashfs_finish_page(output);
+               res = squashfs_decompress(msblk, bio, offset, length, output);
+       } else {
+               res = copy_bio_to_actor(bio, output, offset, length);
        }
 
-       kfree(bh);
-       return length;
-
-block_release:
-       for (; k < b; k++)
-               put_bh(bh[k]);
+out_free_bio:
+       bio_free_pages(bio);
+       bio_put(bio);
+out:
+       if (res < 0)
+               ERROR("Failed to read block 0x%llx: %d\n", index, res);
 
-read_failure:
-       ERROR("squashfs_read_data failed to read block 0x%llx\n",
-                                       (unsigned long long) index);
-       kfree(bh);
-       return -EIO;
+       return res;
 }
index ec8617523e56c1fb771576602048e40a6865e186..1b9ccfd0aa519bff549d9da24cd04d7a130e3e12 100644 (file)
  * decompressor.h
  */
 
+#include <linux/bio.h>
+
 struct squashfs_decompressor {
        void    *(*init)(struct squashfs_sb_info *, void *);
        void    *(*comp_opts)(struct squashfs_sb_info *, void *, int);
        void    (*free)(void *);
        int     (*decompress)(struct squashfs_sb_info *, void *,
-               struct buffer_head **, int, int, int,
-               struct squashfs_page_actor *);
+               struct bio *, int, int, struct squashfs_page_actor *);
        int     id;
        char    *name;
        int     supported;
index c181dee235bb18c2d0d8a1f6e710c15387772088..db9f12a3ea0568aa3f81c50b117a5360248a2dcc 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/types.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/cpumask.h>
@@ -180,14 +180,15 @@ wait:
 }
 
 
-int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
-       int b, int offset, int length, struct squashfs_page_actor *output)
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
+                       int offset, int length,
+                       struct squashfs_page_actor *output)
 {
        int res;
        struct squashfs_stream *stream = msblk->stream;
        struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
        res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
-               bh, b, offset, length, output);
+               bio, offset, length, output);
        put_decomp_stream(decomp_stream, stream);
        if (res < 0)
                ERROR("%s decompression failed, data probably corrupt\n",
index 2a2a2d106440e5192c9193b87fa2f0957453a244..b881b9283b7ffd8be2c447a86eafcf003910b270 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/slab.h>
 #include <linux/percpu.h>
 #include <linux/buffer_head.h>
+#include <linux/local_lock.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -20,7 +21,8 @@
  */
 
 struct squashfs_stream {
-       void            *stream;
+       void                    *stream;
+       local_lock_t    lock;
 };
 
 void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
@@ -41,6 +43,7 @@ void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
                        err = PTR_ERR(stream->stream);
                        goto out;
                }
+               local_lock_init(&stream->lock);
        }
 
        kfree(comp_opts);
@@ -72,15 +75,19 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
        }
 }
 
-int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
-       int b, int offset, int length, struct squashfs_page_actor *output)
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
+       int offset, int length, struct squashfs_page_actor *output)
 {
-       struct squashfs_stream __percpu *percpu =
-                       (struct squashfs_stream __percpu *) msblk->stream;
-       struct squashfs_stream *stream = get_cpu_ptr(percpu);
-       int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
-               offset, length, output);
-       put_cpu_ptr(stream);
+       struct squashfs_stream *stream;
+       int res;
+
+       local_lock(&msblk->stream->lock);
+       stream = this_cpu_ptr(msblk->stream);
+
+       res = msblk->decompressor->decompress(msblk, stream->stream, bio,
+                                             offset, length, output);
+
+       local_unlock(&msblk->stream->lock);
 
        if (res < 0)
                ERROR("%s decompression failed, data probably corrupt\n",
index 550c3e592032bf24874a91ce48a5e99b69ebfdbc..4eb3d083d45eb8a30c99fdd118941465f918d2cd 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/types.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -59,14 +59,15 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
        }
 }
 
-int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
-       int b, int offset, int length, struct squashfs_page_actor *output)
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct bio *bio,
+                       int offset, int length,
+                       struct squashfs_page_actor *output)
 {
        int res;
        struct squashfs_stream *stream = msblk->stream;
 
        mutex_lock(&stream->mutex);
-       res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
+       res = msblk->decompressor->decompress(msblk, stream->stream, bio,
                offset, length, output);
        mutex_unlock(&stream->mutex);
 
index c4e47e0588c74f177db4a637a7bd8c5f9f4e01e7..233d5582fbeec6a3a54e577fc35219790d5fcf46 100644 (file)
@@ -4,7 +4,7 @@
  * Phillip Lougher <phillip@squashfs.org.uk>
  */
 
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
@@ -89,20 +89,23 @@ static void lz4_free(void *strm)
 
 
 static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
-       struct buffer_head **bh, int b, int offset, int length,
+       struct bio *bio, int offset, int length,
        struct squashfs_page_actor *output)
 {
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
        struct squashfs_lz4 *stream = strm;
        void *buff = stream->input, *data;
-       int avail, i, bytes = length, res;
+       int bytes = length, res;
 
-       for (i = 0; i < b; i++) {
-               avail = min(bytes, msblk->devblksize - offset);
-               memcpy(buff, bh[i]->b_data + offset, avail);
+       while (bio_next_segment(bio, &iter_all)) {
+               int avail = min(bytes, ((int)bvec->bv_len) - offset);
+
+               data = page_address(bvec->bv_page) + bvec->bv_offset;
+               memcpy(buff, data + offset, avail);
                buff += avail;
                bytes -= avail;
                offset = 0;
-               put_bh(bh[i]);
        }
 
        res = LZ4_decompress_safe(stream->input, stream->output,
index aa3c3dafc33dc92c5192d49861ad125e22d18d59..97bb7d92ddcdf18f282f8a42646feaa8979a07b2 100644 (file)
@@ -9,7 +9,7 @@
  */
 
 #include <linux/mutex.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/lzo.h>
@@ -63,21 +63,24 @@ static void lzo_free(void *strm)
 
 
 static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
-       struct buffer_head **bh, int b, int offset, int length,
+       struct bio *bio, int offset, int length,
        struct squashfs_page_actor *output)
 {
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
        struct squashfs_lzo *stream = strm;
        void *buff = stream->input, *data;
-       int avail, i, bytes = length, res;
+       int bytes = length, res;
        size_t out_len = output->length;
 
-       for (i = 0; i < b; i++) {
-               avail = min(bytes, msblk->devblksize - offset);
-               memcpy(buff, bh[i]->b_data + offset, avail);
+       while (bio_next_segment(bio, &iter_all)) {
+               int avail = min(bytes, ((int)bvec->bv_len) - offset);
+
+               data = page_address(bvec->bv_page) + bvec->bv_offset;
+               memcpy(buff, data + offset, avail);
                buff += avail;
                bytes -= avail;
                offset = 0;
-               put_bh(bh[i]);
        }
 
        res = lzo1x_decompress_safe(stream->input, (size_t)length,
index 2797763ed046a3482835046ba3b00e4ade3848c3..9783e01c81004106db75a70298d43d27f1f2c334 100644 (file)
@@ -40,8 +40,8 @@ extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
 /* decompressor_xxx.c */
 extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
 extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
-extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
-       int, int, int, struct squashfs_page_actor *);
+extern int squashfs_decompress(struct squashfs_sb_info *, struct bio *,
+                               int, int, struct squashfs_page_actor *);
 extern int squashfs_max_decompressors(void);
 
 /* export.c */
index 4b2f2051a6dc385d7fef6bd35fa446dde4b959f8..e80419aed862b4a3042e4bb8e5162cf0249d294a 100644 (file)
@@ -10,7 +10,7 @@
 
 
 #include <linux/mutex.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/xz.h>
 #include <linux/bitops.h>
@@ -117,11 +117,12 @@ static void squashfs_xz_free(void *strm)
 
 
 static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
-       struct buffer_head **bh, int b, int offset, int length,
+       struct bio *bio, int offset, int length,
        struct squashfs_page_actor *output)
 {
-       enum xz_ret xz_err;
-       int avail, total = 0, k = 0;
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
+       int total = 0, error = 0;
        struct squashfs_xz *stream = strm;
 
        xz_dec_reset(stream->state);
@@ -131,11 +132,23 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
        stream->buf.out_size = PAGE_SIZE;
        stream->buf.out = squashfs_first_page(output);
 
-       do {
-               if (stream->buf.in_pos == stream->buf.in_size && k < b) {
-                       avail = min(length, msblk->devblksize - offset);
+       for (;;) {
+               enum xz_ret xz_err;
+
+               if (stream->buf.in_pos == stream->buf.in_size) {
+                       const void *data;
+                       int avail;
+
+                       if (!bio_next_segment(bio, &iter_all)) {
+                               /* XZ_STREAM_END must be reached. */
+                               error = -EIO;
+                               break;
+                       }
+
+                       avail = min(length, ((int)bvec->bv_len) - offset);
+                       data = page_address(bvec->bv_page) + bvec->bv_offset;
                        length -= avail;
-                       stream->buf.in = bh[k]->b_data + offset;
+                       stream->buf.in = data + offset;
                        stream->buf.in_size = avail;
                        stream->buf.in_pos = 0;
                        offset = 0;
@@ -150,23 +163,17 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
                }
 
                xz_err = xz_dec_run(stream->state, &stream->buf);
-
-               if (stream->buf.in_pos == stream->buf.in_size && k < b)
-                       put_bh(bh[k++]);
-       } while (xz_err == XZ_OK);
+               if (xz_err == XZ_STREAM_END)
+                       break;
+               if (xz_err != XZ_OK) {
+                       error = -EIO;
+                       break;
+               }
+       }
 
        squashfs_finish_page(output);
 
-       if (xz_err != XZ_STREAM_END || k < b)
-               goto out;
-
-       return total + stream->buf.out_pos;
-
-out:
-       for (; k < b; k++)
-               put_bh(bh[k]);
-
-       return -EIO;
+       return error ? error : total + stream->buf.out_pos;
 }
 
 const struct squashfs_decompressor squashfs_xz_comp_ops = {
index f2226afa1625e982c55a9e2ec469340625bd5c10..bcb881ec47f226e1147b961e722543ef1ae17bbf 100644 (file)
@@ -10,7 +10,7 @@
 
 
 #include <linux/mutex.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/zlib.h>
 #include <linux/vmalloc.h>
@@ -50,21 +50,35 @@ static void zlib_free(void *strm)
 
 
 static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
-       struct buffer_head **bh, int b, int offset, int length,
+       struct bio *bio, int offset, int length,
        struct squashfs_page_actor *output)
 {
-       int zlib_err, zlib_init = 0, k = 0;
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
+       int zlib_init = 0, error = 0;
        z_stream *stream = strm;
 
        stream->avail_out = PAGE_SIZE;
        stream->next_out = squashfs_first_page(output);
        stream->avail_in = 0;
 
-       do {
-               if (stream->avail_in == 0 && k < b) {
-                       int avail = min(length, msblk->devblksize - offset);
+       for (;;) {
+               int zlib_err;
+
+               if (stream->avail_in == 0) {
+                       const void *data;
+                       int avail;
+
+                       if (!bio_next_segment(bio, &iter_all)) {
+                               /* Z_STREAM_END must be reached. */
+                               error = -EIO;
+                               break;
+                       }
+
+                       avail = min(length, ((int)bvec->bv_len) - offset);
+                       data = page_address(bvec->bv_page) + bvec->bv_offset;
                        length -= avail;
-                       stream->next_in = bh[k]->b_data + offset;
+                       stream->next_in = data + offset;
                        stream->avail_in = avail;
                        offset = 0;
                }
@@ -78,37 +92,28 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
                if (!zlib_init) {
                        zlib_err = zlib_inflateInit(stream);
                        if (zlib_err != Z_OK) {
-                               squashfs_finish_page(output);
-                               goto out;
+                               error = -EIO;
+                               break;
                        }
                        zlib_init = 1;
                }
 
                zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH);
-
-               if (stream->avail_in == 0 && k < b)
-                       put_bh(bh[k++]);
-       } while (zlib_err == Z_OK);
+               if (zlib_err == Z_STREAM_END)
+                       break;
+               if (zlib_err != Z_OK) {
+                       error = -EIO;
+                       break;
+               }
+       }
 
        squashfs_finish_page(output);
 
-       if (zlib_err != Z_STREAM_END)
-               goto out;
-
-       zlib_err = zlib_inflateEnd(stream);
-       if (zlib_err != Z_OK)
-               goto out;
-
-       if (k < b)
-               goto out;
-
-       return stream->total_out;
-
-out:
-       for (; k < b; k++)
-               put_bh(bh[k]);
+       if (!error)
+               if (zlib_inflateEnd(stream) != Z_OK)
+                       error = -EIO;
 
-       return -EIO;
+       return error ? error : stream->total_out;
 }
 
 const struct squashfs_decompressor squashfs_zlib_comp_ops = {
index b448c2a1d0ed3540c0c7dc78bd4c8fef2c99c858..b7cb1faa652d54ef51a93d661cd7744ea44b4ed0 100644 (file)
@@ -9,7 +9,7 @@
  */
 
 #include <linux/mutex.h>
-#include <linux/buffer_head.h>
+#include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/zstd.h>
 #include <linux/vmalloc.h>
@@ -59,33 +59,44 @@ static void zstd_free(void *strm)
 
 
 static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
-       struct buffer_head **bh, int b, int offset, int length,
+       struct bio *bio, int offset, int length,
        struct squashfs_page_actor *output)
 {
        struct workspace *wksp = strm;
        ZSTD_DStream *stream;
        size_t total_out = 0;
-       size_t zstd_err;
-       int k = 0;
+       int error = 0;
        ZSTD_inBuffer in_buf = { NULL, 0, 0 };
        ZSTD_outBuffer out_buf = { NULL, 0, 0 };
+       struct bvec_iter_all iter_all = {};
+       struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
 
        stream = ZSTD_initDStream(wksp->window_size, wksp->mem, wksp->mem_size);
 
        if (!stream) {
                ERROR("Failed to initialize zstd decompressor\n");
-               goto out;
+               return -EIO;
        }
 
        out_buf.size = PAGE_SIZE;
        out_buf.dst = squashfs_first_page(output);
 
-       do {
-               if (in_buf.pos == in_buf.size && k < b) {
-                       int avail = min(length, msblk->devblksize - offset);
+       for (;;) {
+               size_t zstd_err;
 
+               if (in_buf.pos == in_buf.size) {
+                       const void *data;
+                       int avail;
+
+                       if (!bio_next_segment(bio, &iter_all)) {
+                               error = -EIO;
+                               break;
+                       }
+
+                       avail = min(length, ((int)bvec->bv_len) - offset);
+                       data = page_address(bvec->bv_page) + bvec->bv_offset;
                        length -= avail;
-                       in_buf.src = bh[k]->b_data + offset;
+                       in_buf.src = data + offset;
                        in_buf.size = avail;
                        in_buf.pos = 0;
                        offset = 0;
@@ -97,8 +108,8 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
                                /* Shouldn't run out of pages
                                 * before stream is done.
                                 */
-                               squashfs_finish_page(output);
-                               goto out;
+                               error = -EIO;
+                               break;
                        }
                        out_buf.pos = 0;
                        out_buf.size = PAGE_SIZE;
@@ -107,29 +118,20 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
                total_out -= out_buf.pos;
                zstd_err = ZSTD_decompressStream(stream, &out_buf, &in_buf);
                total_out += out_buf.pos; /* add the additional data produced */
-
-               if (in_buf.pos == in_buf.size && k < b)
-                       put_bh(bh[k++]);
-       } while (zstd_err != 0 && !ZSTD_isError(zstd_err));
-
-       squashfs_finish_page(output);
-
-       if (ZSTD_isError(zstd_err)) {
-               ERROR("zstd decompression error: %d\n",
-                               (int)ZSTD_getErrorCode(zstd_err));
-               goto out;
+               if (zstd_err == 0)
+                       break;
+
+               if (ZSTD_isError(zstd_err)) {
+                       ERROR("zstd decompression error: %d\n",
+                                       (int)ZSTD_getErrorCode(zstd_err));
+                       error = -EIO;
+                       break;
+               }
        }
 
-       if (k < b)
-               goto out;
-
-       return (int)total_out;
-
-out:
-       for (; k < b; k++)
-               put_bh(bh[k]);
+       squashfs_finish_page(output);
 
-       return -EIO;
+       return error ? error : total_out;
 }
 
 const struct squashfs_decompressor squashfs_zstd_comp_ops = {
index 030008796479aa135964995e4fe8a1fad91ede37..b9faa6cafafedb19f86d2f0e0f69eaa4f496bb24 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -22,6 +22,7 @@
 #include <asm/unistd.h>
 
 #include "internal.h"
+#include "mount.h"
 
 /**
  * generic_fillattr - Fill in the basic attributes from the inode struct
@@ -70,11 +71,11 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
 
        memset(stat, 0, sizeof(*stat));
        stat->result_mask |= STATX_BASIC_STATS;
-       request_mask &= STATX_ALL;
        query_flags &= KSTAT_QUERY_FLAGS;
 
        /* allow the fs to override these if it really wants to */
-       if (IS_NOATIME(inode))
+       /* SB_NOATIME means filesystem supplies dummy atime value */
+       if (inode->i_sb->s_flags & SB_NOATIME)
                stat->result_mask &= ~STATX_ATIME;
        if (IS_AUTOMOUNT(inode))
                stat->attributes |= STATX_ATTR_AUTOMOUNT;
@@ -199,6 +200,11 @@ retry:
                goto out;
 
        error = vfs_getattr(&path, stat, request_mask, flags);
+       stat->mnt_id = real_mount(path.mnt)->mnt_id;
+       stat->result_mask |= STATX_MNT_ID;
+       if (path.mnt->mnt_root == path.dentry)
+               stat->attributes |= STATX_ATTR_MOUNT_ROOT;
+       stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
        path_put(&path);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@ -563,6 +569,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
        tmp.stx_rdev_minor = MINOR(stat->rdev);
        tmp.stx_dev_major = MAJOR(stat->dev);
        tmp.stx_dev_minor = MINOR(stat->dev);
+       tmp.stx_mnt_id = stat->mnt_id;
 
        return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
index 4d1ff010bc5afcd6acee1e4dbd736e8bfbd9081a..c6f6f5be5682a97714d676eea9dae9507ae45d21 100644 (file)
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -161,7 +161,7 @@ SYSCALL_DEFINE1(syncfs, int, fd)
 {
        struct fd f = fdget(fd);
        struct super_block *sb;
-       int ret;
+       int ret, ret2;
 
        if (!f.file)
                return -EBADF;
@@ -171,8 +171,10 @@ SYSCALL_DEFINE1(syncfs, int, fd)
        ret = sync_filesystem(sb);
        up_read(&sb->s_umount);
 
+       ret2 = errseq_check_and_advance(&sb->s_wb_err, &f.file->f_sb_err);
+
        fdput(f);
-       return ret;
+       return ret ? ret : ret2;
 }
 
 /**
index aa85f2874a9f79ca5cb1fe387247442773029f99..59dffd5ca51781331a89e33e355bf7e3af160fc0 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2007 SUSE Linux Products GmbH
  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  *
- * Please see Documentation/filesystems/sysfs.txt for more information.
+ * Please see Documentation/filesystems/sysfs.rst for more information.
  */
 
 #define pr_fmt(fmt)    "sysfs: " fmt
index 26bbf960e2a2e28e682e38a6bcc311722d95d416..f275fcda62fb2b4986546e85a6b54fd387e69683 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2007 SUSE Linux Products GmbH
  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  *
- * Please see Documentation/filesystems/sysfs.txt for more information.
+ * Please see Documentation/filesystems/sysfs.rst for more information.
  */
 
 #include <linux/module.h>
index db81cfbab9d60700f3d743ba97a589ea63637bdd..e747c135c1d1f4cbf5ef6040312084b42381a0d3 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2007 SUSE Linux Products GmbH
  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  *
- * Please see Documentation/filesystems/sysfs.txt for more information.
+ * Please see Documentation/filesystems/sysfs.rst for more information.
  */
 
 #include <linux/fs.h>
index c4deecc80f6745977bc48d7c1ed93cfe1409bce1..5603530a1a520d9e481147bc58b9e1b4dc489db1 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2007 SUSE Linux Products GmbH
  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  *
- * Please see Documentation/filesystems/sysfs.txt for more information.
+ * Please see Documentation/filesystems/sysfs.rst for more information.
  */
 
 #include <linux/fs.h>
index d4edf7d9ae10e030cf69103aced6d7fca88ca6f8..b4e23e03fbeba3a7bde6661064efa603a1d189d7 100644 (file)
@@ -28,7 +28,7 @@ config SYSV_FS
          tar" or preferably "info tar").  Note also that this option has
          nothing whatsoever to do with the option "System V IPC". Read about
          the System V file system in
-         <file:Documentation/filesystems/sysv-fs.txt>.
+         <file:Documentation/filesystems/sysv-fs.rst>.
          Saying Y here will enlarge your kernel by about 27 KB.
 
          To compile this as a module, choose M here: the module will be called
index f985a3fbbb36a15e202a7b31c80dff518b5651cf..cc5c0abfd536f867cacc3e749e5651e293f2f986 100644 (file)
@@ -31,15 +31,9 @@ int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
                            u8 *hash)
 {
        const struct ubifs_ch *ch = node;
-       SHASH_DESC_ON_STACK(shash, c->hash_tfm);
-       int err;
-
-       shash->tfm = c->hash_tfm;
 
-       err = crypto_shash_digest(shash, node, le32_to_cpu(ch->len), hash);
-       if (err < 0)
-               return err;
-       return 0;
+       return crypto_shash_tfm_digest(c->hash_tfm, node, le32_to_cpu(ch->len),
+                                      hash);
 }
 
 /**
@@ -53,15 +47,7 @@ int __ubifs_node_calc_hash(const struct ubifs_info *c, const void *node,
 static int ubifs_hash_calc_hmac(const struct ubifs_info *c, const u8 *hash,
                                 u8 *hmac)
 {
-       SHASH_DESC_ON_STACK(shash, c->hmac_tfm);
-       int err;
-
-       shash->tfm = c->hmac_tfm;
-
-       err = crypto_shash_digest(shash, hash, c->hash_len, hmac);
-       if (err < 0)
-               return err;
-       return 0;
+       return crypto_shash_tfm_digest(c->hmac_tfm, hash, c->hash_len, hmac);
 }
 
 /**
index 0f5a480fe264f7b09cd422a1e992924528f9e80d..31288d8fa2ce9a1126c07bedafb227f941893358 100644 (file)
@@ -815,7 +815,7 @@ void ubifs_dump_leb(const struct ubifs_info *c, int lnum)
 
        pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
 
-       buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+       buf = __vmalloc(c->leb_size, GFP_NOFS);
        if (!buf) {
                ubifs_err(c, "cannot allocate memory for dumping LEB %d", lnum);
                return;
index 29826c51883aed2dff4c53960a1f3977a7ac0c38..22bfda158f7fdbcfb1b327736dd66a2fba2a3898 100644 (file)
@@ -1095,7 +1095,7 @@ static int scan_check_cb(struct ubifs_info *c,
                return LPT_SCAN_CONTINUE;
        }
 
-       buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+       buf = __vmalloc(c->leb_size, GFP_NOFS);
        if (!buf)
                return -ENOMEM;
 
index ff5e0411cf2d124335c569e7c587d58ae1c15d62..d76a19e460cd421b863760dfabfa6b4bc0329c9b 100644 (file)
@@ -1596,7 +1596,7 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum)
        if (!dbg_is_chk_lprops(c))
                return 0;
 
-       buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+       buf = p = __vmalloc(c->leb_size, GFP_NOFS);
        if (!buf) {
                ubifs_err(c, "cannot allocate memory for ltab checking");
                return 0;
@@ -1845,7 +1845,7 @@ static void dump_lpt_leb(const struct ubifs_info *c, int lnum)
        void *buf, *p;
 
        pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum);
-       buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+       buf = p = __vmalloc(c->leb_size, GFP_NOFS);
        if (!buf) {
                ubifs_err(c, "cannot allocate memory to dump LPT");
                return;
index 52a85c01397ef9cbe58aba475cced73831abe2b9..911d0555b9f2b1be56fb05f983940e281b636f18 100644 (file)
@@ -68,12 +68,9 @@ static int mst_node_check_hash(const struct ubifs_info *c,
        u8 calc[UBIFS_MAX_HASH_LEN];
        const void *node = mst;
 
-       SHASH_DESC_ON_STACK(shash, c->hash_tfm);
-
-       shash->tfm = c->hash_tfm;
-
-       crypto_shash_digest(shash, node + sizeof(struct ubifs_ch),
-                           UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch), calc);
+       crypto_shash_tfm_digest(c->hash_tfm, node + sizeof(struct ubifs_ch),
+                               UBIFS_MST_NODE_SZ - sizeof(struct ubifs_ch),
+                               calc);
 
        if (ubifs_check_hash(c, expected, calc))
                return -EPERM;
index 283f9eb48410d9d8aeb9f07979ea89eebb825aab..2c294085ffedc8ca851d20a1ad55f34e0b4b9509 100644 (file)
@@ -977,7 +977,7 @@ static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci)
        if (c->no_orphs)
                return 0;
 
-       buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
+       buf = __vmalloc(c->leb_size, GFP_NOFS);
        if (!buf) {
                ubifs_err(c, "cannot allocate memory to check orphans");
                return 0;
index 01fcf79750472b085cc5f6a30cbf85bc1304e08f..b69ffac7e415eeb5fdb26f84730e192b98afcdc7 100644 (file)
@@ -558,7 +558,7 @@ static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud)
        return data == 0xFFFFFFFF;
 }
 
-/* authenticate_sleb_hash and authenticate_sleb_hmac are split out for stack usage */
+/* authenticate_sleb_hash is split out for stack usage */
 static int authenticate_sleb_hash(struct ubifs_info *c, struct shash_desc *log_hash, u8 *hash)
 {
        SHASH_DESC_ON_STACK(hash_desc, c->hash_tfm);
@@ -569,15 +569,6 @@ static int authenticate_sleb_hash(struct ubifs_info *c, struct shash_desc *log_h
        return crypto_shash_final(hash_desc, hash);
 }
 
-static int authenticate_sleb_hmac(struct ubifs_info *c, u8 *hash, u8 *hmac)
-{
-       SHASH_DESC_ON_STACK(hmac_desc, c->hmac_tfm);
-
-       hmac_desc->tfm = c->hmac_tfm;
-
-       return crypto_shash_digest(hmac_desc, hash, c->hash_len, hmac);
-}
-
 /**
  * authenticate_sleb - authenticate one scan LEB
  * @c: UBIFS file-system description object
@@ -618,7 +609,8 @@ static int authenticate_sleb(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
                        if (err)
                                goto out;
 
-                       err = authenticate_sleb_hmac(c, hash, hmac);
+                       err = crypto_shash_tfm_digest(c->hmac_tfm, hash,
+                                                     c->hash_len, hmac);
                        if (err)
                                goto out;
 
index 6848de581ce1d9b8d223b23e00890a9e35350abb..26e1a49f3ba7956aaf22c6dfdef914d5110287da 100644 (file)
@@ -9,7 +9,7 @@ config UDF_FS
          compatible with standard unix file systems, it is also suitable for
          removable USB disks. Say Y if you intend to mount DVD discs or CDRW's
          written in packet mode, or if you want to use UDF for removable USB
-         disks. Please read <file:Documentation/filesystems/udf.txt>.
+         disks. Please read <file:Documentation/filesystems/udf.rst>.
 
          To compile this file system support as a module, choose M here: the
          module will be called udf.
index e875bc5668ee75c4484d585dda2d8f1826277270..adaba8e8b326e69f1ddf9678fffd5f49c47b9803 100644 (file)
@@ -195,10 +195,9 @@ static int udf_readpage(struct file *file, struct page *page)
        return mpage_readpage(page, udf_get_block);
 }
 
-static int udf_readpages(struct file *file, struct address_space *mapping,
-                       struct list_head *pages, unsigned nr_pages)
+static void udf_readahead(struct readahead_control *rac)
 {
-       return mpage_readpages(mapping, pages, nr_pages, udf_get_block);
+       mpage_readahead(rac, udf_get_block);
 }
 
 static int udf_write_begin(struct file *file, struct address_space *mapping,
@@ -234,7 +233,7 @@ static sector_t udf_bmap(struct address_space *mapping, sector_t block)
 
 const struct address_space_operations udf_aops = {
        .readpage       = udf_readpage,
-       .readpages      = udf_readpages,
+       .readahead      = udf_readahead,
        .writepage      = udf_writepage,
        .writepages     = udf_writepages,
        .write_begin    = udf_write_begin,
index 1d17ce98cb808cc720dca65fcd147645e28dbb29..b7b927502d6e43c8619b351a4e819e184b1c445d 100644 (file)
@@ -95,13 +95,13 @@ long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
                goto out;
        }
 
-       if (flags & ~AT_SYMLINK_NOFOLLOW)
+       if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
                goto out;
 
        if (filename == NULL && dfd != AT_FDCWD) {
                struct fd f;
 
-               if (flags & AT_SYMLINK_NOFOLLOW)
+               if (flags)
                        goto out;
 
                f = fdget(dfd);
@@ -117,6 +117,8 @@ long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
 
                if (!(flags & AT_SYMLINK_NOFOLLOW))
                        lookup_flags |= LOOKUP_FOLLOW;
+               if (flags & AT_EMPTY_PATH)
+                       lookup_flags |= LOOKUP_EMPTY;
 retry:
                error = user_path_at(dfd, filename, lookup_flags, &path);
                if (error)
index d98bea308fd7f1bc234764c689116d9ffb27b25a..5ab3bbec810873d260923b159cce06823a2dfd48 100644 (file)
@@ -329,6 +329,8 @@ rollback:
 
 /**
  * fsverity_ioctl_enable() - enable verity on a file
+ * @filp: file to enable verity on
+ * @uarg: user pointer to fsverity_enable_arg
  *
  * Enable fs-verity on a file.  See the "FS_IOC_ENABLE_VERITY" section of
  * Documentation/filesystems/fsverity.rst for the documentation.
index 74768cf539daf5f1d2c4ccf9157fa931cfea4b22..e96d99d5145e1da25fe07f9e26650d57df28b2b6 100644 (file)
@@ -61,7 +61,7 @@ struct merkle_tree_params {
        u64 level_start[FS_VERITY_MAX_LEVELS];
 };
 
-/**
+/*
  * fsverity_info - cached verity metadata for an inode
  *
  * When a verity file is first opened, an instance of this struct is allocated
@@ -134,7 +134,7 @@ void __init fsverity_check_hash_algs(void);
 
 /* init.c */
 
-extern void __printf(3, 4) __cold
+void __printf(3, 4) __cold
 fsverity_msg(const struct inode *inode, const char *level,
             const char *fmt, ...);
 
index 05049b68c74553fb6b865818c989b00e5c2da36e..df409a5682edf9a568219d541a0902e9abb91bb9 100644 (file)
@@ -11,6 +11,8 @@
 
 /**
  * fsverity_ioctl_measure() - get a verity file's measurement
+ * @filp: file to get measurement of
+ * @_uarg: user pointer to fsverity_digest
  *
  * Retrieve the file measurement that the kernel is enforcing for reads from a
  * verity file.  See the "FS_IOC_MEASURE_VERITY" section of
index c5fe6948e26290befc4932dd98d97cb01f61a760..d007db0c9304d1f7461080ce6332c7022158efd1 100644 (file)
@@ -330,6 +330,7 @@ EXPORT_SYMBOL_GPL(fsverity_prepare_setattr);
 
 /**
  * fsverity_cleanup_inode() - free the inode's verity info, if present
+ * @inode: an inode being evicted
  *
  * Filesystems must call this on inode eviction to free ->i_verity_info.
  */
index c8b255232de54391c006d4a9df9ed10e7d29c005..b14ed96387ece05635780ef34e267729fe27ccb5 100644 (file)
@@ -28,6 +28,9 @@ static struct key *fsverity_keyring;
 
 /**
  * fsverity_verify_signature() - check a verity file's signature
+ * @vi: the file's fsverity_info
+ * @desc: the file's fsverity_descriptor
+ * @desc_size: size of @desc
  *
  * If the file's fs-verity descriptor includes a signature of the file
  * measurement, verify it against the certificates in the fs-verity keyring.
index e0cb62da38644b34fd95ec9ab5fea808cc892b43..a8b68c6f663d123c18ae1238dd3b1186ae846968 100644 (file)
@@ -179,6 +179,7 @@ out:
 
 /**
  * fsverity_verify_page() - verify a data page
+ * @page: the page to verity
  *
  * Verify a page that has just been read from a verity file.  The page must be a
  * pagecache page that is still locked and not yet uptodate.
@@ -206,6 +207,7 @@ EXPORT_SYMBOL_GPL(fsverity_verify_page);
 #ifdef CONFIG_BLOCK
 /**
  * fsverity_verify_bio() - verify a 'read' bio that has just completed
+ * @bio: the bio to verify
  *
  * Verify a set of pages that have just been read from a verity file.  The pages
  * must be pagecache pages that are still locked and not yet uptodate.  Pages
@@ -264,6 +266,7 @@ EXPORT_SYMBOL_GPL(fsverity_verify_bio);
 
 /**
  * fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue
+ * @work: the work to enqueue
  *
  * Enqueue verification work for asynchronous processing.
  */
index e13265e65871f4fc1f09e13bb410c3f44d2d6882..91608d9bfc6aad9b346d8e4622590e99339d8a6d 100644 (file)
@@ -876,6 +876,9 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
        struct simple_xattr *new_xattr = NULL;
        int err = 0;
 
+       if (removed_size)
+               *removed_size = -1;
+
        /* value == NULL means remove */
        if (value) {
                new_xattr = simple_xattr_alloc(value, size);
@@ -914,9 +917,6 @@ int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
                list_add(&new_xattr->list, &xattrs->head);
                xattr = NULL;
        }
-
-       if (removed_size)
-               *removed_size = -1;
 out:
        spin_unlock(&xattrs->lock);
        if (xattr) {
index 1da94237a8cf1e55a39786ab3c7185b8fd516001..f1366475c389c0a7de3a1f4c9288cc4ae6abb919 100644 (file)
@@ -48,7 +48,7 @@ __kmem_vmalloc(size_t size, xfs_km_flags_t flags)
        if (flags & KM_NOFS)
                nofs_flag = memalloc_nofs_save();
 
-       ptr = __vmalloc(size, lflags, PAGE_KERNEL);
+       ptr = __vmalloc(size, lflags);
 
        if (flags & KM_NOFS)
                memalloc_nofs_restore(nofs_flag);
index 9d9cebf187268ac51a58a8618286fbcdf5966b98..1fd4fb7a607cbcd77c61fcfb85a1ad3be1ccee94 100644 (file)
@@ -621,14 +621,11 @@ xfs_vm_readpage(
        return iomap_readpage(page, &xfs_read_iomap_ops);
 }
 
-STATIC int
-xfs_vm_readpages(
-       struct file             *unused,
-       struct address_space    *mapping,
-       struct list_head        *pages,
-       unsigned                nr_pages)
+STATIC void
+xfs_vm_readahead(
+       struct readahead_control        *rac)
 {
-       return iomap_readpages(mapping, pages, nr_pages, &xfs_read_iomap_ops);
+       iomap_readahead(rac, &xfs_read_iomap_ops);
 }
 
 static int
@@ -644,7 +641,7 @@ xfs_iomap_swapfile_activate(
 
 const struct address_space_operations xfs_address_space_operations = {
        .readpage               = xfs_vm_readpage,
-       .readpages              = xfs_vm_readpages,
+       .readahead              = xfs_vm_readahead,
        .writepage              = xfs_vm_writepage,
        .writepages             = xfs_vm_writepages,
        .set_page_dirty         = iomap_set_page_dirty,
index 9ec3eaf1c618fb708394188aa969477d95da4da8..65538d18e64fb9bdf49986d3dc3faaf7a0afab90 100644 (file)
@@ -477,7 +477,7 @@ _xfs_buf_map_pages(
                nofs_flag = memalloc_nofs_save();
                do {
                        bp->b_addr = vm_map_ram(bp->b_pages, bp->b_page_count,
-                                               -1, PAGE_KERNEL);
+                                               -1);
                        if (bp->b_addr)
                                break;
                        vm_unmap_aliases();
index 3ce9829a6936da8967062b5bf6ca02f62b748c9a..dba874a61fc5c3859b0b29f6f92f3643d0bdccf6 100644 (file)
@@ -78,10 +78,9 @@ static int zonefs_readpage(struct file *unused, struct page *page)
        return iomap_readpage(page, &zonefs_iomap_ops);
 }
 
-static int zonefs_readpages(struct file *unused, struct address_space *mapping,
-                           struct list_head *pages, unsigned int nr_pages)
+static void zonefs_readahead(struct readahead_control *rac)
 {
-       return iomap_readpages(mapping, pages, nr_pages, &zonefs_iomap_ops);
+       iomap_readahead(rac, &zonefs_iomap_ops);
 }
 
 /*
@@ -128,7 +127,7 @@ static int zonefs_writepages(struct address_space *mapping,
 
 static const struct address_space_operations zonefs_file_aops = {
        .readpage               = zonefs_readpage,
-       .readpages              = zonefs_readpages,
+       .readahead              = zonefs_readahead,
        .writepage              = zonefs_writepage,
        .writepages             = zonefs_writepages,
        .set_page_dirty         = iomap_set_page_dirty,
index 4c74b1c1d13b847c0567260e188c0ad68903b8db..58046ddc08d02bcf7b862b39447481c528c56faf 100644 (file)
@@ -17,8 +17,9 @@
        ((unlikely(pgd_none(*(p4d))) && __pud_alloc(mm, p4d, address)) ? \
                NULL : pud_offset(p4d, address))
 
-#define p4d_alloc(mm, pgd, address)    (pgd)
-#define p4d_offset(pgd, start)         (pgd)
+#define p4d_alloc(mm, pgd, address)            (pgd)
+#define p4d_alloc_track(mm, pgd, address, mask)        (pgd)
+#define p4d_offset(pgd, start)                 (pgd)
 
 #ifndef __ASSEMBLY__
 static inline int p4d_none(p4d_t p4d)
index 34785c0f57b0ebb07b6d151713bd3bd667519141..5a80f8e543008a9a167a86e32d75e200fb680102 100644 (file)
@@ -25,15 +25,6 @@ extern __wsum csum_partial(const void *buff, int len, __wsum sum);
  */
 extern __wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum);
 
-/*
- * the same as csum_partial_copy, but copies from user space.
- *
- * here even more important to align src and dst on a 32-bit (or even
- * better 64-bit) boundary
- */
-extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
-                                       int len, __wsum sum, int *csum_err);
-
 #ifndef csum_partial_copy_nocheck
 #define csum_partial_copy_nocheck(src, dst, len, sum)  \
        csum_partial_copy((src), (dst), (len), (sum))
index 329b8c8ca703b670c149e7eaf840682c523f9d7c..db7df7daa0d8a758760abc4b39b782b910183314 100644 (file)
@@ -491,6 +491,10 @@ static inline int arch_unmap_one(struct mm_struct *mm,
 #define flush_tlb_fix_spurious_fault(vma, address) flush_tlb_page(vma, address)
 #endif
 
+#ifndef pgprot_nx
+#define pgprot_nx(prot)        (prot)
+#endif
+
 #ifndef pgprot_noncached
 #define pgprot_noncached(prot) (prot)
 #endif
@@ -1209,6 +1213,29 @@ static inline bool arch_has_pfn_modify_check(void)
 # define PAGE_KERNEL_EXEC PAGE_KERNEL
 #endif
 
+/*
+ * Page Table Modification bits for pgtbl_mod_mask.
+ *
+ * These are used by the p?d_alloc_track*() set of functions an in the generic
+ * vmalloc/ioremap code to track at which page-table levels entries have been
+ * modified. Based on that the code can better decide when vmalloc and ioremap
+ * mapping changes need to be synchronized to other page-tables in the system.
+ */
+#define                __PGTBL_PGD_MODIFIED    0
+#define                __PGTBL_P4D_MODIFIED    1
+#define                __PGTBL_PUD_MODIFIED    2
+#define                __PGTBL_PMD_MODIFIED    3
+#define                __PGTBL_PTE_MODIFIED    4
+
+#define                PGTBL_PGD_MODIFIED      BIT(__PGTBL_PGD_MODIFIED)
+#define                PGTBL_P4D_MODIFIED      BIT(__PGTBL_P4D_MODIFIED)
+#define                PGTBL_PUD_MODIFIED      BIT(__PGTBL_PUD_MODIFIED)
+#define                PGTBL_PMD_MODIFIED      BIT(__PGTBL_PMD_MODIFIED)
+#define                PGTBL_PTE_MODIFIED      BIT(__PGTBL_PTE_MODIFIED)
+
+/* Page-Table Modification Mask */
+typedef unsigned int pgtbl_mod_mask;
+
 #endif /* !__ASSEMBLY__ */
 
 #ifndef io_remap_pfn_range
index d1779d442aa51a4d223ae8fd855c8fd93aa34077..66397ed10acb787d19ad524269096d3d28ff5a33 100644 (file)
@@ -53,6 +53,9 @@ extern char __ctors_start[], __ctors_end[];
 /* Start and end of .opd section - used for function descriptors. */
 extern char __start_opd[], __end_opd[];
 
+/* Start and end of instrumentation protected text section */
+extern char __noinstr_text_start[], __noinstr_text_end[];
+
 extern __visible const void __nosave_begin, __nosave_end;
 
 /* Function descriptor handling (if any).  Override in asm/sections.h */
index 2388737395502101e308ec7c77b51b7f422dc194..5aa8705df87e730dcc454574ff5a133b0c1f939e 100644 (file)
@@ -48,7 +48,7 @@
   #ifdef CONFIG_NEED_MULTIPLE_NODES
     #define cpumask_of_node(node)      ((node) == 0 ? cpu_online_mask : cpu_none_mask)
   #else
-    #define cpumask_of_node(node)      ((void)node, cpu_online_mask)
+    #define cpumask_of_node(node)      ((void)(node), cpu_online_mask)
   #endif
 #endif
 #ifndef pcibus_to_node
index 71e387a5fe90e3fbe34835ee3edf08a26454439c..db600ef218d7d2e56b6fdf895b1958f80fa05462 100644 (file)
        . = ALIGN((align));                                             \
        __end_rodata = .;
 
+/*
+ * Non-instrumentable text section
+ */
+#define NOINSTR_TEXT                                                   \
+               ALIGN_FUNCTION();                                       \
+               __noinstr_text_start = .;                               \
+               *(.noinstr.text)                                        \
+               __noinstr_text_end = .;
+
 /*
  * .text section. Map to function alignment to avoid address changes
  * during second ld run in second ld pass when generating System.map
 #define TEXT_TEXT                                                      \
                ALIGN_FUNCTION();                                       \
                *(.text.hot TEXT_MAIN .text.fixup .text.unlikely)       \
+               NOINSTR_TEXT                                            \
                *(.text..refcount)                                      \
                *(.ref.text)                                            \
        MEM_KEEP(init.text*)                                            \
index d873f999b3347ba49fd7b256e76b5cf9383897bb..2b4d2b06ccbdfd17803e85bff4c09428fb60fb23 100644 (file)
@@ -157,7 +157,7 @@ static inline int crypto_has_acomp(const char *alg_name, u32 type, u32 mask)
 {
        type &= ~CRYPTO_ALG_TYPE_MASK;
        type |= CRYPTO_ALG_TYPE_ACOMPRESS;
-       mask |= CRYPTO_ALG_TYPE_MASK;
+       mask |= CRYPTO_ALG_TYPE_ACOMPRESS_MASK;
 
        return crypto_has_alg(alg_name, type, mask);
 }
index e115f9215ed57455b0a60ab060247c79e9d25b8a..00a9cf98debe2c80c535667e5a8adc92662d1770 100644 (file)
@@ -125,6 +125,8 @@ int crypto_inst_setname(struct crypto_instance *inst, const char *name,
 void crypto_init_queue(struct crypto_queue *queue, unsigned int max_qlen);
 int crypto_enqueue_request(struct crypto_queue *queue,
                           struct crypto_async_request *request);
+void crypto_enqueue_request_head(struct crypto_queue *queue,
+                                struct crypto_async_request *request);
 struct crypto_async_request *crypto_dequeue_request(struct crypto_queue *queue);
 static inline unsigned int crypto_queue_len(struct crypto_queue *queue)
 {
index 8c9af21efce1d94c39bdd1c4358f861f15a53843..c4165126937e45e3d8d4b13f1e57ff1aa18fc580 100644 (file)
@@ -184,11 +184,7 @@ static inline size_t drbg_max_addtl(struct drbg_state *drbg)
 static inline size_t drbg_max_requests(struct drbg_state *drbg)
 {
        /* SP800-90A requires 2**48 maximum requests before reseeding */
-#if (__BITS_PER_LONG == 32)
-       return SIZE_MAX;
-#else
-       return (1UL<<48);
-#endif
+       return (1<<20);
 }
 
 /*
index e29cd67f93c702a7b44f591d588b6e1aa119e497..3f06e40d063a6efdf9951d4e499a0979a8ba7812 100644 (file)
@@ -24,7 +24,9 @@
  * @idling: the engine is entering idle state
  * @busy: request pump is busy
  * @running: the engine is on working
- * @cur_req_prepared: current request is prepared
+ * @retry_support: indication that the hardware allows re-execution
+ * of a failed backlog request
+ * crypto-engine, in head position to keep order
  * @list: link with the global crypto engine list
  * @queue_lock: spinlock to syncronise access to request queue
  * @queue: the crypto queue of the engine
@@ -35,6 +37,8 @@
  * @unprepare_crypt_hardware: there are currently no more requests on the
  * queue so the subsystem notifies the driver that it may relax the
  * hardware by issuing this call
+ * @do_batch_requests: execute a batch of requests. Depends on multiple
+ * requests support.
  * @kworker: kthread worker struct for request pump
  * @pump_requests: work struct for scheduling work to the request pump
  * @priv_data: the engine private data
@@ -45,7 +49,8 @@ struct crypto_engine {
        bool                    idling;
        bool                    busy;
        bool                    running;
-       bool                    cur_req_prepared;
+
+       bool                    retry_support;
 
        struct list_head        list;
        spinlock_t              queue_lock;
@@ -56,6 +61,8 @@ struct crypto_engine {
 
        int (*prepare_crypt_hardware)(struct crypto_engine *engine);
        int (*unprepare_crypt_hardware)(struct crypto_engine *engine);
+       int (*do_batch_requests)(struct crypto_engine *engine);
+
 
        struct kthread_worker           *kworker;
        struct kthread_work             pump_requests;
@@ -102,6 +109,10 @@ void crypto_finalize_skcipher_request(struct crypto_engine *engine,
 int crypto_engine_start(struct crypto_engine *engine);
 int crypto_engine_stop(struct crypto_engine *engine);
 struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt);
+struct crypto_engine *crypto_engine_alloc_init_and_set(struct device *dev,
+                                                      bool retry_support,
+                                                      int (*cbk_do_batch)(struct crypto_engine *engine),
+                                                      bool rt, int qlen);
 int crypto_engine_exit(struct crypto_engine *engine);
 
 #endif /* _CRYPTO_ENGINE_H */
index cee446c59497c610c4c72398b615424ecc13b8bf..4829d2367eda876522777f78685f79a8c6a7bec1 100644 (file)
@@ -855,6 +855,25 @@ int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
 int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
                        unsigned int len, u8 *out);
 
+/**
+ * crypto_shash_tfm_digest() - calculate message digest for buffer
+ * @tfm: hash transformation object
+ * @data: see crypto_shash_update()
+ * @len: see crypto_shash_update()
+ * @out: see crypto_shash_final()
+ *
+ * This is a simplified version of crypto_shash_digest() for users who don't
+ * want to allocate their own hash descriptor (shash_desc).  Instead,
+ * crypto_shash_tfm_digest() takes a hash transformation object (crypto_shash)
+ * directly, and it allocates a hash descriptor on the stack internally.
+ * Note that this stack allocation may be fairly large.
+ *
+ * Context: Any context.
+ * Return: 0 on success; < 0 if an error occurred.
+ */
+int crypto_shash_tfm_digest(struct crypto_shash *tfm, const u8 *data,
+                           unsigned int len, u8 *out);
+
 /**
  * crypto_shash_export() - extract operational state for message digest
  * @desc: reference to the operational state handle whose state is exported
index 5c2132c7190095bc941dbd2b335234d8d9e12f22..10753ff71d46da26de69f80dc1c1b46c8104d2da 100644 (file)
@@ -113,6 +113,16 @@ extern int crypto_sha512_update(struct shash_desc *desc, const u8 *data,
 extern int crypto_sha512_finup(struct shash_desc *desc, const u8 *data,
                               unsigned int len, u8 *hash);
 
+/*
+ * An implementation of SHA-1's compression function.  Don't use in new code!
+ * You shouldn't be using SHA-1, and even if you *have* to use SHA-1, this isn't
+ * the correct way to hash something with SHA-1 (use crypto_shash instead).
+ */
+#define SHA1_DIGEST_WORDS      (SHA1_DIGEST_SIZE / 4)
+#define SHA1_WORKSPACE_WORDS   16
+void sha1_init(__u32 *buf);
+void sha1_transform(__u32 *digest, const char *data, __u32 *W);
+
 /*
  * Stand-alone implementation of the SHA256 algorithm. It is designed to
  * have as little dependencies as possible so it can be used in the
@@ -123,7 +133,7 @@ extern int crypto_sha512_finup(struct shash_desc *desc, const u8 *data,
  * For details see lib/crypto/sha256.c
  */
 
-static inline int sha256_init(struct sha256_state *sctx)
+static inline void sha256_init(struct sha256_state *sctx)
 {
        sctx->state[0] = SHA256_H0;
        sctx->state[1] = SHA256_H1;
@@ -134,14 +144,11 @@ static inline int sha256_init(struct sha256_state *sctx)
        sctx->state[6] = SHA256_H6;
        sctx->state[7] = SHA256_H7;
        sctx->count = 0;
-
-       return 0;
 }
-extern int sha256_update(struct sha256_state *sctx, const u8 *input,
-                        unsigned int length);
-extern int sha256_final(struct sha256_state *sctx, u8 *hash);
+void sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len);
+void sha256_final(struct sha256_state *sctx, u8 *out);
 
-static inline int sha224_init(struct sha256_state *sctx)
+static inline void sha224_init(struct sha256_state *sctx)
 {
        sctx->state[0] = SHA224_H0;
        sctx->state[1] = SHA224_H1;
@@ -152,11 +159,8 @@ static inline int sha224_init(struct sha256_state *sctx)
        sctx->state[6] = SHA224_H6;
        sctx->state[7] = SHA224_H7;
        sctx->count = 0;
-
-       return 0;
 }
-extern int sha224_update(struct sha256_state *sctx, const u8 *input,
-                        unsigned int length);
-extern int sha224_final(struct sha256_state *sctx, u8 *hash);
+void sha224_update(struct sha256_state *sctx, const u8 *data, unsigned int len);
+void sha224_final(struct sha256_state *sctx, u8 *out);
 
 #endif
index cea60cff80bd870f5c9153d07277e2d47919b413..6ded110783ae87dd97c40edc42b83ad0a08431f6 100644 (file)
@@ -22,14 +22,16 @@ static inline int sha224_base_init(struct shash_desc *desc)
 {
        struct sha256_state *sctx = shash_desc_ctx(desc);
 
-       return sha224_init(sctx);
+       sha224_init(sctx);
+       return 0;
 }
 
 static inline int sha256_base_init(struct shash_desc *desc)
 {
        struct sha256_state *sctx = shash_desc_ctx(desc);
 
-       return sha256_init(sctx);
+       sha256_init(sctx);
+       return 0;
 }
 
 static inline int sha256_base_do_update(struct shash_desc *desc,
index 59494df0f55b66e22ceebdb10255cccd61f97a8e..56d6a5c6e353e5d47386d94f5c18a887c5205548 100644 (file)
@@ -5,12 +5,15 @@
 #ifndef __LINUX_ARM_SMCCC_H
 #define __LINUX_ARM_SMCCC_H
 
+#include <linux/init.h>
 #include <uapi/linux/const.h>
 
 /*
  * This file provides common defines for ARM SMC Calling Convention as
  * specified in
- * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 C
  */
 
 #define ARM_SMCCC_STD_CALL             _AC(0,U)
@@ -56,6 +59,7 @@
 
 #define ARM_SMCCC_VERSION_1_0          0x10000
 #define ARM_SMCCC_VERSION_1_1          0x10001
+#define ARM_SMCCC_VERSION_1_2          0x10002
 
 #define ARM_SMCCC_VERSION_FUNC_ID                                      \
        ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,                         \
@@ -97,6 +101,19 @@ enum arm_smccc_conduit {
  */
 enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void);
 
+/**
+ * arm_smccc_get_version()
+ *
+ * Returns the version to be used for SMCCCv1.1 or later.
+ *
+ * When SMCCCv1.1 or above is not present, returns SMCCCv1.0, but this
+ * does not imply the presence of firmware or a valid conduit. Caller
+ * handling SMCCCv1.0 must determine the conduit by other means.
+ */
+u32 arm_smccc_get_version(void);
+
+void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit);
+
 /**
  * struct arm_smccc_res - Result from SMC/HVC call
  * @a0-a3 result values from registers 0 to 3
@@ -314,10 +331,14 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
  */
 #define arm_smccc_1_1_hvc(...) __arm_smccc_1_1(SMCCC_HVC_INST, __VA_ARGS__)
 
-/* Return codes defined in ARM DEN 0070A */
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
 #define SMCCC_RET_SUCCESS                      0
 #define SMCCC_RET_NOT_SUPPORTED                        -1
 #define SMCCC_RET_NOT_REQUIRED                 -2
+#define SMCCC_RET_INVALID_PARAMETER            -3
 
 /*
  * Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
index 15b765a181b8504f10ba7b827f9cbe3ddbbfae28..22fb11e2d2e04d93312441c2c680c29033f94034 100644 (file)
@@ -272,14 +272,6 @@ void buffer_init(void);
  * inline definitions
  */
 
-static inline void attach_page_buffers(struct page *page,
-               struct buffer_head *head)
-{
-       get_page(page);
-       SetPagePrivate(page);
-       set_page_private(page, (unsigned long)head);
-}
-
 static inline void get_bh(struct buffer_head *bh)
 {
         atomic_inc(&bh->b_count);
index ecce0f43c73acde0bfce88d459d838aa12398d9b..027d7e4a853b98e40169f5d937b7a98bc2fd18d0 100644 (file)
@@ -251,6 +251,10 @@ extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct
 extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
 extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
 extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
+static inline bool perfmon_capable(void)
+{
+       return capable(CAP_PERFMON) || capable(CAP_SYS_ADMIN);
+}
 
 /* audit system wants to get cap info from files as well */
 extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
index 0480ba4db592981411ee149bfe5cbe3e98fef855..e90100c0de72e4a998b93db9b8e2d5322d3328cf 100644 (file)
@@ -402,8 +402,15 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
                       unsigned long bitmap_size);
 long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
                       unsigned long bitmap_size);
-int copy_siginfo_from_user32(kernel_siginfo_t *to, const struct compat_siginfo __user *from);
-int copy_siginfo_to_user32(struct compat_siginfo __user *to, const kernel_siginfo_t *from);
+void copy_siginfo_to_external32(struct compat_siginfo *to,
+               const struct kernel_siginfo *from);
+int copy_siginfo_from_user32(kernel_siginfo_t *to,
+               const struct compat_siginfo __user *from);
+int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
+               const kernel_siginfo_t *from);
+#ifndef copy_siginfo_to_user32
+#define copy_siginfo_to_user32 __copy_siginfo_to_user32
+#endif
 int get_compat_sigevent(struct sigevent *event,
                const struct compat_sigevent __user *u_event);
 
index 333a6695a918c3dcea8402c054bb7a6069640cb0..790c0c6b8552bd70dea93d897c47f008e7243ab1 100644 (file)
@@ -42,3 +42,7 @@
  * compilers, like ICC.
  */
 #define barrier() __asm__ __volatile__("" : : : "memory")
+
+#if __has_feature(shadow_call_stack)
+# define __noscs       __attribute__((__no_sanitize__("shadow-call-stack")))
+#endif
index 448c91bf543b7394bdbcde5c8b8458df131c4c33..6325d64e3c3b1f250f858d9c6cb9a54cb99f8824 100644 (file)
@@ -120,12 +120,65 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
 /* Annotate a C jump table to allow objtool to follow the code flow */
 #define __annotate_jump_table __section(.rodata..c_jump_table)
 
+#ifdef CONFIG_DEBUG_ENTRY
+/* Begin/end of an instrumentation safe region */
+#define instrumentation_begin() ({                                     \
+       asm volatile("%c0:\n\t"                                         \
+                    ".pushsection .discard.instr_begin\n\t"            \
+                    ".long %c0b - .\n\t"                               \
+                    ".popsection\n\t" : : "i" (__COUNTER__));          \
+})
+
+/*
+ * Because instrumentation_{begin,end}() can nest, objtool validation considers
+ * _begin() a +1 and _end() a -1 and computes a sum over the instructions.
+ * When the value is greater than 0, we consider instrumentation allowed.
+ *
+ * There is a problem with code like:
+ *
+ * noinstr void foo()
+ * {
+ *     instrumentation_begin();
+ *     ...
+ *     if (cond) {
+ *             instrumentation_begin();
+ *             ...
+ *             instrumentation_end();
+ *     }
+ *     bar();
+ *     instrumentation_end();
+ * }
+ *
+ * If instrumentation_end() would be an empty label, like all the other
+ * annotations, the inner _end(), which is at the end of a conditional block,
+ * would land on the instruction after the block.
+ *
+ * If we then consider the sum of the !cond path, we'll see that the call to
+ * bar() is with a 0-value, even though, we meant it to happen with a positive
+ * value.
+ *
+ * To avoid this, have _end() be a NOP instruction, this ensures it will be
+ * part of the condition block and does not escape.
+ */
+#define instrumentation_end() ({                                       \
+       asm volatile("%c0: nop\n\t"                                     \
+                    ".pushsection .discard.instr_end\n\t"              \
+                    ".long %c0b - .\n\t"                               \
+                    ".popsection\n\t" : : "i" (__COUNTER__));          \
+})
+#endif /* CONFIG_DEBUG_ENTRY */
+
 #else
 #define annotate_reachable()
 #define annotate_unreachable()
 #define __annotate_jump_table
 #endif
 
+#ifndef instrumentation_begin
+#define instrumentation_begin()                do { } while(0)
+#define instrumentation_end()          do { } while(0)
+#endif
+
 #ifndef ASM_UNREACHABLE
 # define ASM_UNREACHABLE
 #endif
index e970f97a7fcb1c60f993f6e4eb13e6d947d85e60..6fcf73200b6708833093f4f20c03f1d90a99e3ae 100644 (file)
@@ -118,6 +118,10 @@ struct ftrace_likely_data {
 #define notrace                        __attribute__((__no_instrument_function__))
 #endif
 
+/* Section for code which can't be instrumented at all */
+#define noinstr                                                                \
+       noinline notrace __attribute((__section__(".noinstr.text")))
+
 /*
  * it doesn't make sense on ARM (currently the only user of __naked)
  * to trace naked functions because then mcount is called without
@@ -193,6 +197,10 @@ struct ftrace_likely_data {
 # define randomized_struct_fields_end
 #endif
 
+#ifndef __noscs
+# define __noscs
+#endif
+
 #ifndef asm_volatile_goto
 #define asm_volatile_goto(x...) asm goto(x)
 #endif
index fa9490a8874c8212987727e17186c9faf6950ac1..2e8c69b43c6424b6224fe46cf5c8e24365bc5b51 100644 (file)
@@ -13,7 +13,7 @@
  *
  * configfs Copyright (C) 2005 Oracle.  All rights reserved.
  *
- * Please read Documentation/filesystems/configfs/configfs.txt before using
+ * Please read Documentation/filesystems/configfs.rst before using
  * the configfs interface, ESPECIALLY the parts about reference counts and
  * item destructors.
  */
index 7a140f4e5d0cf31eb9d1b6434532e9b8b501f379..75dd20650fbe4d1af4b7633f8eb873ae049a52a9 100644 (file)
@@ -134,7 +134,7 @@ static inline int con_debug_leave(void)
  */
 
 #define CON_PRINTBUFFER        (1)
-#define CON_CONSDEV    (2) /* Last on the command line */
+#define CON_CONSDEV    (2) /* Preferred console, /dev/console */
 #define CON_ENABLED    (4)
 #define CON_BOOT       (8)
 #define CON_ANYTIME    (16) /* Safe to call when cpu is offline */
index beaed2dc269e8e4b955b66f8f82e28a9fa9bcffe..52692587f7fe671b82b23e9805aec00081ae9c6c 100644 (file)
@@ -144,18 +144,8 @@ static inline void get_online_cpus(void) { cpus_read_lock(); }
 static inline void put_online_cpus(void) { cpus_read_unlock(); }
 
 #ifdef CONFIG_PM_SLEEP_SMP
-int __freeze_secondary_cpus(int primary, bool suspend);
-static inline int freeze_secondary_cpus(int primary)
-{
-       return __freeze_secondary_cpus(primary, true);
-}
-
-static inline int disable_nonboot_cpus(void)
-{
-       return __freeze_secondary_cpus(0, false);
-}
-
-void enable_nonboot_cpus(void);
+extern int freeze_secondary_cpus(int primary);
+extern void thaw_secondary_cpus(void);
 
 static inline int suspend_disable_secondary_cpus(void)
 {
@@ -168,12 +158,11 @@ static inline int suspend_disable_secondary_cpus(void)
 }
 static inline void suspend_enable_secondary_cpus(void)
 {
-       return enable_nonboot_cpus();
+       return thaw_secondary_cpus();
 }
 
 #else /* !CONFIG_PM_SLEEP_SMP */
-static inline int disable_nonboot_cpus(void) { return 0; }
-static inline void enable_nonboot_cpus(void) {}
+static inline void thaw_secondary_cpus(void) {}
 static inline int suspend_disable_secondary_cpus(void) { return 0; }
 static inline void suspend_enable_secondary_cpus(void) { }
 #endif /* !CONFIG_PM_SLEEP_SMP */
index f7240251a949e551e514e027993a21c05cdc11f4..67d5950bd878ce53ab7473aa360d1855671ae417 100644 (file)
@@ -330,7 +330,7 @@ struct cpufreq_driver {
         *
         * get_intermediate should return a stable intermediate frequency
         * platform wants to switch to and target_intermediate() should set CPU
-        * to to that frequency, before jumping to the frequency corresponding
+        * to that frequency, before jumping to the frequency corresponding
         * to 'index'. Core will take care of sending notifications and driver
         * doesn't have to handle them in target_intermediate() or
         * target_index().
index 4664fc1871de68606995e4c998d710bf278e572e..bc156285d09769f0bb87ff68428b61f148f0951b 100644 (file)
@@ -97,8 +97,6 @@ extern void unregister_oldmem_pfn_is_ram(void);
 static inline bool is_kdump_kernel(void) { return 0; }
 #endif /* CONFIG_CRASH_DUMP */
 
-extern unsigned long saved_max_pfn;
-
 /* Device Dump information to be filled by drivers */
 struct vmcoredd_data {
        char dump_name[VMCOREDD_MAX_NAME_BYTES]; /* Unique name of the dump */
diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h
deleted file mode 100644 (file)
index f6ba4c3..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __CRYPTOHASH_H
-#define __CRYPTOHASH_H
-
-#include <uapi/linux/types.h>
-
-#define SHA_DIGEST_WORDS 5
-#define SHA_MESSAGE_BYTES (512 /*bits*/ / 8)
-#define SHA_WORKSPACE_WORDS 16
-
-void sha_init(__u32 *buf);
-void sha_transform(__u32 *digest, const char *data, __u32 *W);
-
-#endif
index fa35b52e0002e03a4a2476f2f854daade6e6742d..d02f32b7514e647c7a16efa3cbf01e35b9e8066e 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/fs.h>
-#include <linux/bpf-cgroup.h>
 
 #define DEVCG_ACC_MKNOD 1
 #define DEVCG_ACC_READ  2
 #define DEVCG_DEV_CHAR  2
 #define DEVCG_DEV_ALL   4  /* this represents all devices */
 
-#ifdef CONFIG_CGROUP_DEVICE
-int devcgroup_check_permission(short type, u32 major, u32 minor,
-                              short access);
-#else
-static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
-                                            short access)
-{ return 0; }
-#endif
 
 #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
+int devcgroup_check_permission(short type, u32 major, u32 minor,
+                              short access);
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 {
        short type, access = 0;
@@ -51,6 +44,9 @@ static inline int devcgroup_inode_mknod(int mode, dev_t dev)
        if (!S_ISBLK(mode) && !S_ISCHR(mode))
                return 0;
 
+       if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
+               return 0;
+
        if (S_ISBLK(mode))
                type = DEVCG_DEV_BLOCK;
        else
@@ -61,6 +57,9 @@ static inline int devcgroup_inode_mknod(int mode, dev_t dev)
 }
 
 #else
+static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
+                              short access)
+{ return 0; }
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 { return 0; }
 static inline int devcgroup_inode_mknod(int mode, dev_t dev)
index 9430d01c0c3d339f9402935d0d164223070c2602..2c6495f72f79a44e2a548633aaaa34388975065f 100644 (file)
@@ -39,6 +39,7 @@
 #define EFI_WRITE_PROTECTED    ( 8 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_OUT_OF_RESOURCES   ( 9 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_NOT_FOUND          (14 | (1UL << (BITS_PER_LONG-1)))
+#define EFI_TIMEOUT            (18 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_ABORTED            (21 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
 
@@ -379,8 +380,8 @@ typedef union {
 
 typedef struct {
        efi_guid_t guid;
-       const char *name;
        unsigned long *ptr;
+       const char name[16];
 } efi_config_table_type_t;
 
 #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL)
@@ -426,6 +427,7 @@ typedef struct {
        u32 tables;
 } efi_system_table_32_t;
 
+typedef union efi_simple_text_input_protocol efi_simple_text_input_protocol_t;
 typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
 
 typedef union {
@@ -434,7 +436,7 @@ typedef union {
                unsigned long fw_vendor;        /* physical addr of CHAR16 vendor string */
                u32 fw_revision;
                unsigned long con_in_handle;
-               unsigned long con_in;
+               efi_simple_text_input_protocol_t *con_in;
                unsigned long con_out_handle;
                efi_simple_text_output_protocol_t *con_out;
                unsigned long stderr_handle;
index e3649b3e970e63d325958e2c31aebbe1ef16586e..5d5b0321da0bd81db3afa2bbe802252ca7e58a94 100644 (file)
@@ -2,6 +2,7 @@
 #ifndef _LINUX_ELF_H
 #define _LINUX_ELF_H
 
+#include <linux/types.h>
 #include <asm/elf.h>
 #include <uapi/linux/elf.h>
 
@@ -21,6 +22,9 @@
        SET_PERSONALITY(ex)
 #endif
 
+#define ELF32_GNU_PROPERTY_ALIGN       4
+#define ELF64_GNU_PROPERTY_ALIGN       8
+
 #if ELF_CLASS == ELFCLASS32
 
 extern Elf32_Dyn _DYNAMIC [];
@@ -31,6 +35,7 @@ extern Elf32_Dyn _DYNAMIC [];
 #define elf_addr_t     Elf32_Off
 #define Elf_Half       Elf32_Half
 #define Elf_Word       Elf32_Word
+#define ELF_GNU_PROPERTY_ALIGN ELF32_GNU_PROPERTY_ALIGN
 
 #else
 
@@ -42,6 +47,7 @@ extern Elf64_Dyn _DYNAMIC [];
 #define elf_addr_t     Elf64_Off
 #define Elf_Half       Elf64_Half
 #define Elf_Word       Elf64_Word
+#define ELF_GNU_PROPERTY_ALIGN ELF64_GNU_PROPERTY_ALIGN
 
 #endif
 
@@ -56,4 +62,41 @@ static inline int elf_coredump_extra_notes_write(struct coredump_params *cprm) {
 extern int elf_coredump_extra_notes_size(void);
 extern int elf_coredump_extra_notes_write(struct coredump_params *cprm);
 #endif
+
+/*
+ * NT_GNU_PROPERTY_TYPE_0 header:
+ * Keep this internal until/unless there is an agreed UAPI definition.
+ * pr_type values (GNU_PROPERTY_*) are public and defined in the UAPI header.
+ */
+struct gnu_property {
+       u32 pr_type;
+       u32 pr_datasz;
+};
+
+struct arch_elf_state;
+
+#ifndef CONFIG_ARCH_USE_GNU_PROPERTY
+static inline int arch_parse_elf_property(u32 type, const void *data,
+                                         size_t datasz, bool compat,
+                                         struct arch_elf_state *arch)
+{
+       return 0;
+}
+#else
+extern int arch_parse_elf_property(u32 type, const void *data, size_t datasz,
+                                  bool compat, struct arch_elf_state *arch);
+#endif
+
+#ifdef CONFIG_ARCH_HAVE_ELF_PROT
+int arch_elf_adjust_prot(int prot, const struct arch_elf_state *state,
+                        bool has_interp, bool is_interp);
+#else
+static inline int arch_elf_adjust_prot(int prot,
+                                      const struct arch_elf_state *state,
+                                      bool has_interp, bool is_interp)
+{
+       return prot;
+}
+#endif
+
 #endif /* _LINUX_ELF_H */
index 3049a6c06d9e1151e4fe5317f737faee0a2137c7..b79fa9bb7359531f294dafb7550bd9fc0208987c 100644 (file)
@@ -47,8 +47,7 @@
  * Directory entry modification events - reported only to directory
  * where entry is modified and not to a watching parent.
  */
-#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE | \
-                                FAN_DIR_MODIFY)
+#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE)
 
 /* Events that can only be reported with data type FSNOTIFY_EVENT_INODE */
 #define FANOTIFY_INODE_EVENTS  (FANOTIFY_DIRENT_EVENTS | \
index 9b5aa5c483ccb59be7ea7be2fe856b2995c64344..ec45fd7992c957d32fac92f1b33050b903155246 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/sched.h>
 #include <linux/capability.h>
-#include <linux/cryptohash.h>
 #include <linux/set_memory.h>
 #include <linux/kallsyms.h>
 #include <linux/if_vlan.h>
 #include <linux/vmalloc.h>
+#include <crypto/sha.h>
 
 #include <net/sch_generic.h>
 
@@ -746,7 +746,7 @@ static inline u32 bpf_prog_insn_size(const struct bpf_prog *prog)
 static inline u32 bpf_prog_tag_scratch_size(const struct bpf_prog *prog)
 {
        return round_up(bpf_prog_insn_size(prog) +
-                       sizeof(__be64) + 1, SHA_MESSAGE_BYTES);
+                       sizeof(__be64) + 1, SHA1_BLOCK_SIZE);
 }
 
 static inline unsigned int bpf_prog_size(unsigned int proglen)
index 02d3ca2d959853f3e6b7847a07aea0f81192e37f..303cda600e56acce0f05e5508a9843bbc828de61 100644 (file)
        static void __used __section(.discard.func_stack_frame_non_standard) \
                *__func_stack_frame_non_standard_##func = func
 
+/*
+ * This macro indicates that the following intra-function call is valid.
+ * Any non-annotated intra-function call will cause objtool to issue a warning.
+ */
+#define ANNOTATE_INTRA_FUNCTION_CALL                           \
+       999:                                                    \
+       .pushsection .discard.intra_function_calls;             \
+       .long 999b;                                             \
+       .popsection;
+
 #else /* !CONFIG_STACK_VALIDATION */
 
 #define STACK_FRAME_NON_STANDARD(func)
+#define ANNOTATE_INTRA_FUNCTION_CALL
 
 #endif /* CONFIG_STACK_VALIDATION */
 
index 45cc10cdf6ddd760aeadc92d255f65b132ed67cc..ef6acd2062eb272cbdf963aa136e401f82a9021c 100644 (file)
@@ -292,6 +292,7 @@ enum positive_aop_returns {
 struct page;
 struct address_space;
 struct writeback_control;
+struct readahead_control;
 
 /*
  * Write life time hint values.
@@ -375,6 +376,7 @@ struct address_space_operations {
         */
        int (*readpages)(struct file *filp, struct address_space *mapping,
                        struct list_head *pages, unsigned nr_pages);
+       void (*readahead)(struct readahead_control *);
 
        int (*write_begin)(struct file *, struct address_space *mapping,
                                loff_t pos, unsigned len, unsigned flags,
@@ -976,6 +978,7 @@ struct file {
 #endif /* #ifdef CONFIG_EPOLL */
        struct address_space    *f_mapping;
        errseq_t                f_wb_err;
+       errseq_t                f_sb_err; /* for syncfs */
 } __randomize_layout
   __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
 
@@ -1520,6 +1523,9 @@ struct super_block {
        /* Being remounted read-only */
        int s_readonly_remount;
 
+       /* per-sb errseq_t for reporting writeback errors via syncfs */
+       errseq_t s_wb_err;
+
        /* AIO completions deferred from interrupt context */
        struct workqueue_struct *s_dio_done_wq;
        struct hlist_head s_pins;
@@ -1721,7 +1727,11 @@ extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct ino
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
-extern int vfs_whiteout(struct inode *, struct dentry *);
+
+static inline int vfs_whiteout(struct inode *dir, struct dentry *dentry)
+{
+       return vfs_mknod(dir, dentry, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+}
 
 extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
                                  int open_flag);
@@ -2827,6 +2837,18 @@ static inline errseq_t filemap_sample_wb_err(struct address_space *mapping)
        return errseq_sample(&mapping->wb_err);
 }
 
+/**
+ * file_sample_sb_err - sample the current errseq_t to test for later errors
+ * @mapping: mapping to be sampled
+ *
+ * Grab the most current superblock-level errseq_t value for the given
+ * struct file.
+ */
+static inline errseq_t file_sample_sb_err(struct file *file)
+{
+       return errseq_sample(&file->f_path.dentry->d_sb->s_wb_err);
+}
+
 static inline int filemap_nr_thps(struct address_space *mapping)
 {
 #ifdef CONFIG_READ_ONLY_THP_FOR_FS
index e6c3e4c61dad76c7f4da82df9c2262aa1f646655..5f24fcbfbfb4467717a339e66c07b6c35e614587 100644 (file)
@@ -85,7 +85,7 @@ struct p_log {
  * Superblock creation fills in ->root whereas reconfiguration begins with this
  * already set.
  *
- * See Documentation/filesystems/mount_api.txt
+ * See Documentation/filesystems/mount_api.rst
  */
 struct fs_context {
        const struct fs_context_operations *ops;
index d5ba431b5d63cdb7fa150c43710f7b88df25f6ad..ce0b5fbf239d7481daccb673317fb9a13934456b 100644 (file)
@@ -6,7 +6,7 @@
  *
  * NOTE!!! See:
  *
- *     Documentation/filesystems/caching/backend-api.txt
+ *     Documentation/filesystems/caching/backend-api.rst
  *
  * for a description of the cache backend interface declared here.
  */
@@ -454,7 +454,7 @@ static inline void fscache_object_lookup_error(struct fscache_object *object)
  * Set the maximum size an object is permitted to reach, implying the highest
  * byte that may be written.  Intended to be called by the attr_changed() op.
  *
- * See Documentation/filesystems/caching/backend-api.txt for a complete
+ * See Documentation/filesystems/caching/backend-api.rst for a complete
  * description.
  */
 static inline
index ad044c0cb1f3bf511db2e3b72518a15f8b3c3247..a1c928fe98e7cb9f6b0eef78bdbbb134dcdacf4e 100644 (file)
@@ -6,7 +6,7 @@
  *
  * NOTE!!! See:
  *
- *     Documentation/filesystems/caching/netfs-api.txt
+ *     Documentation/filesystems/caching/netfs-api.rst
  *
  * for a description of the network filesystem interface declared here.
  */
@@ -233,7 +233,7 @@ extern void __fscache_enable_cookie(struct fscache_cookie *, const void *, loff_
  *
  * Register a filesystem as desiring caching services if they're available.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -253,7 +253,7 @@ int fscache_register_netfs(struct fscache_netfs *netfs)
  * Indicate that a filesystem no longer desires caching services for the
  * moment.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -270,7 +270,7 @@ void fscache_unregister_netfs(struct fscache_netfs *netfs)
  * Acquire a specific cache referral tag that can be used to select a specific
  * cache in which to cache an index.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -288,7 +288,7 @@ struct fscache_cache_tag *fscache_lookup_cache_tag(const char *name)
  *
  * Release a reference to a cache referral tag previously looked up.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -315,7 +315,7 @@ void fscache_release_cache_tag(struct fscache_cache_tag *tag)
  * that can be used to locate files.  This is done by requesting a cookie for
  * each index in the path to the file.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -351,7 +351,7 @@ struct fscache_cookie *fscache_acquire_cookie(
  * provided to update the auxiliary data in the cache before the object is
  * disconnected.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -394,7 +394,7 @@ int fscache_check_consistency(struct fscache_cookie *cookie,
  * cookie.  The auxiliary data on the cookie will be updated first if @aux_data
  * is set.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -410,7 +410,7 @@ void fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data)
  *
  * Permit data-storage cache objects to be pinned in the cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -425,7 +425,7 @@ int fscache_pin_cookie(struct fscache_cookie *cookie)
  *
  * Permit data-storage cache objects to be unpinned from the cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -441,7 +441,7 @@ void fscache_unpin_cookie(struct fscache_cookie *cookie)
  * changed.  This includes the data size.  These attributes will be obtained
  * through the get_attr() cookie definition op.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -463,7 +463,7 @@ int fscache_attr_changed(struct fscache_cookie *cookie)
  *
  * This can be called with spinlocks held.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -479,7 +479,7 @@ void fscache_invalidate(struct fscache_cookie *cookie)
  *
  * Wait for the invalidation of an object to complete.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -498,7 +498,7 @@ void fscache_wait_on_invalidate(struct fscache_cookie *cookie)
  * cookie so that a write to that object within the space can always be
  * honoured.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -533,7 +533,7 @@ int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size)
  * Else, if the page is unbacked, -ENODATA is returned and a block may have
  * been allocated in the cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -582,7 +582,7 @@ int fscache_read_or_alloc_page(struct fscache_cookie *cookie,
  * regard to different pages, the return values are prioritised in that order.
  * Any pages submitted for reading are removed from the pages list.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -617,7 +617,7 @@ int fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
  * Else, a block will be allocated if one wasn't already, and 0 will be
  * returned
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -667,7 +667,7 @@ void fscache_readpages_cancel(struct fscache_cookie *cookie,
  * be cleared at the completion of the write to indicate the success or failure
  * of the operation.  Note that the completion may happen before the return.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -693,7 +693,7 @@ int fscache_write_page(struct fscache_cookie *cookie,
  * Note that this cannot cancel any outstanding I/O operations between this
  * page and the cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -711,7 +711,7 @@ void fscache_uncache_page(struct fscache_cookie *cookie,
  *
  * Ask the cache if a page is being written to the cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
@@ -731,7 +731,7 @@ bool fscache_check_page_write(struct fscache_cookie *cookie,
  * Ask the cache to wake us up when a page is no longer being written to the
  * cache.
  *
- * See Documentation/filesystems/caching/netfs-api.txt for a complete
+ * See Documentation/filesystems/caching/netfs-api.rst for a complete
  * description.
  */
 static inline
index e3c2d2a155250a474bc9ffc43fdefef86d57e2dd..2862ca5fea334808a4438272315e01338d41b724 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/mm.h>
+#include <linux/parser.h>
 #include <linux/slab.h>
 #include <uapi/linux/fscrypt.h>
 
 #define FS_CRYPTO_BLOCK_SIZE           16
 
+union fscrypt_context;
 struct fscrypt_info;
+struct seq_file;
 
 struct fscrypt_str {
        unsigned char *name;
@@ -56,10 +59,12 @@ struct fscrypt_name {
 struct fscrypt_operations {
        unsigned int flags;
        const char *key_prefix;
-       int (*get_context)(struct inode *, void *, size_t);
-       int (*set_context)(struct inode *, const void *, size_t, void *);
-       bool (*dummy_context)(struct inode *);
-       bool (*empty_dir)(struct inode *);
+       int (*get_context)(struct inode *inode, void *ctx, size_t len);
+       int (*set_context)(struct inode *inode, const void *ctx, size_t len,
+                          void *fs_data);
+       const union fscrypt_context *(*get_dummy_context)(
+               struct super_block *sb);
+       bool (*empty_dir)(struct inode *inode);
        unsigned int max_namelen;
        bool (*has_stable_inodes)(struct super_block *sb);
        void (*get_ino_and_lblk_bits)(struct super_block *sb,
@@ -75,6 +80,7 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode)
 /**
  * fscrypt_needs_contents_encryption() - check whether an inode needs
  *                                      contents encryption
+ * @inode: the inode to check
  *
  * Return: %true iff the inode is an encrypted regular file and the kernel was
  * built with fscrypt support.
@@ -87,10 +93,12 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
        return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode);
 }
 
-static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
+static inline const union fscrypt_context *
+fscrypt_get_dummy_context(struct super_block *sb)
 {
-       return inode->i_sb->s_cop->dummy_context &&
-               inode->i_sb->s_cop->dummy_context(inode);
+       if (!sb->s_cop->get_dummy_context)
+               return NULL;
+       return sb->s_cop->get_dummy_context(sb);
 }
 
 /*
@@ -106,22 +114,21 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry)
 }
 
 /* crypto.c */
-extern void fscrypt_enqueue_decrypt_work(struct work_struct *);
-
-extern struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
-                                                    unsigned int len,
-                                                    unsigned int offs,
-                                                    gfp_t gfp_flags);
-extern int fscrypt_encrypt_block_inplace(const struct inode *inode,
-                                        struct page *page, unsigned int len,
-                                        unsigned int offs, u64 lblk_num,
-                                        gfp_t gfp_flags);
-
-extern int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
-                                           unsigned int offs);
-extern int fscrypt_decrypt_block_inplace(const struct inode *inode,
-                                        struct page *page, unsigned int len,
-                                        unsigned int offs, u64 lblk_num);
+void fscrypt_enqueue_decrypt_work(struct work_struct *);
+
+struct page *fscrypt_encrypt_pagecache_blocks(struct page *page,
+                                             unsigned int len,
+                                             unsigned int offs,
+                                             gfp_t gfp_flags);
+int fscrypt_encrypt_block_inplace(const struct inode *inode, struct page *page,
+                                 unsigned int len, unsigned int offs,
+                                 u64 lblk_num, gfp_t gfp_flags);
+
+int fscrypt_decrypt_pagecache_blocks(struct page *page, unsigned int len,
+                                    unsigned int offs);
+int fscrypt_decrypt_block_inplace(const struct inode *inode, struct page *page,
+                                 unsigned int len, unsigned int offs,
+                                 u64 lblk_num);
 
 static inline bool fscrypt_is_bounce_page(struct page *page)
 {
@@ -133,78 +140,90 @@ static inline struct page *fscrypt_pagecache_page(struct page *bounce_page)
        return (struct page *)page_private(bounce_page);
 }
 
-extern void fscrypt_free_bounce_page(struct page *bounce_page);
+void fscrypt_free_bounce_page(struct page *bounce_page);
 
 /* policy.c */
-extern int fscrypt_ioctl_set_policy(struct file *, const void __user *);
-extern int fscrypt_ioctl_get_policy(struct file *, void __user *);
-extern int fscrypt_ioctl_get_policy_ex(struct file *, void __user *);
-extern int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
-extern int fscrypt_has_permitted_context(struct inode *, struct inode *);
-extern int fscrypt_inherit_context(struct inode *, struct inode *,
-                                       void *, bool);
+int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg);
+int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg);
+int fscrypt_ioctl_get_policy_ex(struct file *filp, void __user *arg);
+int fscrypt_ioctl_get_nonce(struct file *filp, void __user *arg);
+int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
+int fscrypt_inherit_context(struct inode *parent, struct inode *child,
+                           void *fs_data, bool preload);
+
+struct fscrypt_dummy_context {
+       const union fscrypt_context *ctx;
+};
+
+int fscrypt_set_test_dummy_encryption(struct super_block *sb,
+                                     const substring_t *arg,
+                                     struct fscrypt_dummy_context *dummy_ctx);
+void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
+                                       struct super_block *sb);
+static inline void
+fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
+{
+       kfree(dummy_ctx->ctx);
+       dummy_ctx->ctx = NULL;
+}
+
 /* keyring.c */
-extern void fscrypt_sb_free(struct super_block *sb);
-extern int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
-extern int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
-extern int fscrypt_ioctl_remove_key_all_users(struct file *filp,
-                                             void __user *arg);
-extern int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
+void fscrypt_sb_free(struct super_block *sb);
+int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
+int fscrypt_ioctl_remove_key(struct file *filp, void __user *arg);
+int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg);
+int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg);
 
 /* keysetup.c */
-extern int fscrypt_get_encryption_info(struct inode *);
-extern void fscrypt_put_encryption_info(struct inode *);
-extern void fscrypt_free_inode(struct inode *);
-extern int fscrypt_drop_inode(struct inode *inode);
+int fscrypt_get_encryption_info(struct inode *inode);
+void fscrypt_put_encryption_info(struct inode *inode);
+void fscrypt_free_inode(struct inode *inode);
+int fscrypt_drop_inode(struct inode *inode);
 
 /* fname.c */
-extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
-                               int lookup, struct fscrypt_name *);
+int fscrypt_setup_filename(struct inode *inode, const struct qstr *iname,
+                          int lookup, struct fscrypt_name *fname);
 
 static inline void fscrypt_free_filename(struct fscrypt_name *fname)
 {
        kfree(fname->crypto_buf.name);
 }
 
-extern int fscrypt_fname_alloc_buffer(const struct inode *, u32,
-                               struct fscrypt_str *);
-extern void fscrypt_fname_free_buffer(struct fscrypt_str *);
-extern int fscrypt_fname_disk_to_usr(const struct inode *inode,
-                                    u32 hash, u32 minor_hash,
-                                    const struct fscrypt_str *iname,
-                                    struct fscrypt_str *oname);
-extern bool fscrypt_match_name(const struct fscrypt_name *fname,
-                              const u8 *de_name, u32 de_name_len);
-extern u64 fscrypt_fname_siphash(const struct inode *dir,
-                                const struct qstr *name);
+int fscrypt_fname_alloc_buffer(const struct inode *inode, u32 max_encrypted_len,
+                              struct fscrypt_str *crypto_str);
+void fscrypt_fname_free_buffer(struct fscrypt_str *crypto_str);
+int fscrypt_fname_disk_to_usr(const struct inode *inode,
+                             u32 hash, u32 minor_hash,
+                             const struct fscrypt_str *iname,
+                             struct fscrypt_str *oname);
+bool fscrypt_match_name(const struct fscrypt_name *fname,
+                       const u8 *de_name, u32 de_name_len);
+u64 fscrypt_fname_siphash(const struct inode *dir, const struct qstr *name);
 
 /* bio.c */
-extern void fscrypt_decrypt_bio(struct bio *);
-extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t,
-                                unsigned int);
+void fscrypt_decrypt_bio(struct bio *bio);
+int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk,
+                         sector_t pblk, unsigned int len);
 
 /* hooks.c */
-extern int fscrypt_file_open(struct inode *inode, struct file *filp);
-extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
-                                 struct dentry *dentry);
-extern int __fscrypt_prepare_rename(struct inode *old_dir,
-                                   struct dentry *old_dentry,
-                                   struct inode *new_dir,
-                                   struct dentry *new_dentry,
-                                   unsigned int flags);
-extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
-                                   struct fscrypt_name *fname);
-extern int fscrypt_prepare_setflags(struct inode *inode,
-                                   unsigned int oldflags, unsigned int flags);
-extern int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
-                                    unsigned int max_len,
-                                    struct fscrypt_str *disk_link);
-extern int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
-                                    unsigned int len,
-                                    struct fscrypt_str *disk_link);
-extern const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
-                                      unsigned int max_size,
-                                      struct delayed_call *done);
+int fscrypt_file_open(struct inode *inode, struct file *filp);
+int __fscrypt_prepare_link(struct inode *inode, struct inode *dir,
+                          struct dentry *dentry);
+int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry,
+                            struct inode *new_dir, struct dentry *new_dentry,
+                            unsigned int flags);
+int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry,
+                            struct fscrypt_name *fname);
+int fscrypt_prepare_setflags(struct inode *inode,
+                            unsigned int oldflags, unsigned int flags);
+int __fscrypt_prepare_symlink(struct inode *dir, unsigned int len,
+                             unsigned int max_len,
+                             struct fscrypt_str *disk_link);
+int __fscrypt_encrypt_symlink(struct inode *inode, const char *target,
+                             unsigned int len, struct fscrypt_str *disk_link);
+const char *fscrypt_get_symlink(struct inode *inode, const void *caddr,
+                               unsigned int max_size,
+                               struct delayed_call *done);
 static inline void fscrypt_set_ops(struct super_block *sb,
                                   const struct fscrypt_operations *s_cop)
 {
@@ -222,9 +241,10 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
        return false;
 }
 
-static inline bool fscrypt_dummy_context_enabled(struct inode *inode)
+static inline const union fscrypt_context *
+fscrypt_get_dummy_context(struct super_block *sb)
 {
-       return false;
+       return NULL;
 }
 
 static inline void fscrypt_handle_d_move(struct dentry *dentry)
@@ -319,6 +339,20 @@ static inline int fscrypt_inherit_context(struct inode *parent,
        return -EOPNOTSUPP;
 }
 
+struct fscrypt_dummy_context {
+};
+
+static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
+                                                     char sep,
+                                                     struct super_block *sb)
+{
+}
+
+static inline void
+fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
+{
+}
+
 /* keyring.c */
 static inline void fscrypt_sb_free(struct super_block *sb)
 {
@@ -504,7 +538,7 @@ static inline void fscrypt_set_ops(struct super_block *sb,
 #endif /* !CONFIG_FS_ENCRYPTION */
 
 /**
- * fscrypt_require_key - require an inode's encryption key
+ * fscrypt_require_key() - require an inode's encryption key
  * @inode: the inode we need the key for
  *
  * If the inode is encrypted, set up its encryption key if not already done.
@@ -530,7 +564,8 @@ static inline int fscrypt_require_key(struct inode *inode)
 }
 
 /**
- * fscrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory
+ * fscrypt_prepare_link() - prepare to link an inode into a possibly-encrypted
+ *                         directory
  * @old_dentry: an existing dentry for the inode being linked
  * @dir: the target directory
  * @dentry: negative dentry for the target filename
@@ -557,7 +592,8 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry,
 }
 
 /**
- * fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories
+ * fscrypt_prepare_rename() - prepare for a rename between possibly-encrypted
+ *                           directories
  * @old_dir: source directory
  * @old_dentry: dentry for source file
  * @new_dir: target directory
@@ -590,7 +626,8 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir,
 }
 
 /**
- * fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory
+ * fscrypt_prepare_lookup() - prepare to lookup a name in a possibly-encrypted
+ *                           directory
  * @dir: directory being searched
  * @dentry: filename being looked up
  * @fname: (output) the name to use to search the on-disk directory
@@ -623,7 +660,8 @@ static inline int fscrypt_prepare_lookup(struct inode *dir,
 }
 
 /**
- * fscrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes
+ * fscrypt_prepare_setattr() - prepare to change a possibly-encrypted inode's
+ *                            attributes
  * @dentry: dentry through which the inode is being changed
  * @attr: attributes to change
  *
@@ -648,7 +686,7 @@ static inline int fscrypt_prepare_setattr(struct dentry *dentry,
 }
 
 /**
- * fscrypt_prepare_symlink - prepare to create a possibly-encrypted symlink
+ * fscrypt_prepare_symlink() - prepare to create a possibly-encrypted symlink
  * @dir: directory in which the symlink is being created
  * @target: plaintext symlink target
  * @len: length of @target excluding null terminator
@@ -676,7 +714,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir,
                                          unsigned int max_len,
                                          struct fscrypt_str *disk_link)
 {
-       if (IS_ENCRYPTED(dir) || fscrypt_dummy_context_enabled(dir))
+       if (IS_ENCRYPTED(dir) || fscrypt_get_dummy_context(dir->i_sb) != NULL)
                return __fscrypt_prepare_symlink(dir, len, max_len, disk_link);
 
        disk_link->name = (unsigned char *)target;
@@ -687,7 +725,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir,
 }
 
 /**
- * fscrypt_encrypt_symlink - encrypt the symlink target if needed
+ * fscrypt_encrypt_symlink() - encrypt the symlink target if needed
  * @inode: symlink inode
  * @target: plaintext symlink target
  * @len: length of @target excluding null terminator
index ecc604e61d61b9ffcc96946904f5608e837168b5..78201a6d35f66d8b077f0d9faab6bc8be1315750 100644 (file)
@@ -121,23 +121,23 @@ static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
 
 /* enable.c */
 
-extern int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
+int fsverity_ioctl_enable(struct file *filp, const void __user *arg);
 
 /* measure.c */
 
-extern int fsverity_ioctl_measure(struct file *filp, void __user *arg);
+int fsverity_ioctl_measure(struct file *filp, void __user *arg);
 
 /* open.c */
 
-extern int fsverity_file_open(struct inode *inode, struct file *filp);
-extern int fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr);
-extern void fsverity_cleanup_inode(struct inode *inode);
+int fsverity_file_open(struct inode *inode, struct file *filp);
+int fsverity_prepare_setattr(struct dentry *dentry, struct iattr *attr);
+void fsverity_cleanup_inode(struct inode *inode);
 
 /* verify.c */
 
-extern bool fsverity_verify_page(struct page *page);
-extern void fsverity_verify_bio(struct bio *bio);
-extern void fsverity_enqueue_verify_work(struct work_struct *work);
+bool fsverity_verify_page(struct page *page);
+void fsverity_verify_bio(struct bio *bio);
+void fsverity_enqueue_verify_work(struct work_struct *work);
 
 #else /* !CONFIG_FS_VERITY */
 
@@ -200,6 +200,7 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
 
 /**
  * fsverity_active() - do reads from the inode need to go through fs-verity?
+ * @inode: inode to check
  *
  * This checks whether ->i_verity_info has been set.
  *
@@ -207,6 +208,8 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
  * be verified or not.  Don't use IS_VERITY() for this purpose; it's subject to
  * a race condition where the file is being read concurrently with
  * FS_IOC_ENABLE_VERITY completing.  (S_VERITY is set before ->i_verity_info.)
+ *
+ * Return: true if reads need to go through fs-verity, otherwise false
  */
 static inline bool fsverity_active(const struct inode *inode)
 {
index ccda97dc7f8b916fb62942057d3316e9abaa2b0b..0abd9a1d2852d932489ba1f5b3473f5c777d6cfe 100644 (file)
@@ -2,15 +2,6 @@
 #ifndef _LINUX_FTRACE_IRQ_H
 #define _LINUX_FTRACE_IRQ_H
 
-
-#ifdef CONFIG_FTRACE_NMI_ENTER
-extern void arch_ftrace_nmi_enter(void);
-extern void arch_ftrace_nmi_exit(void);
-#else
-static inline void arch_ftrace_nmi_enter(void) { }
-static inline void arch_ftrace_nmi_exit(void) { }
-#endif
-
 #ifdef CONFIG_HWLAT_TRACER
 extern bool trace_hwlat_callback_enabled;
 extern void trace_hwlat_callback(bool enter);
@@ -22,12 +13,10 @@ static inline void ftrace_nmi_enter(void)
        if (trace_hwlat_callback_enabled)
                trace_hwlat_callback(true);
 #endif
-       arch_ftrace_nmi_enter();
 }
 
 static inline void ftrace_nmi_exit(void)
 {
-       arch_ftrace_nmi_exit();
 #ifdef CONFIG_HWLAT_TRACER
        if (trace_hwlat_callback_enabled)
                trace_hwlat_callback(false);
index 7c8b82f6928881f64bb14f9c93a66978c8650074..e07cf853aa161aac5a23b79d645e15d1ca1d2f63 100644 (file)
@@ -2,31 +2,28 @@
 #ifndef LINUX_HARDIRQ_H
 #define LINUX_HARDIRQ_H
 
+#include <linux/context_tracking_state.h>
 #include <linux/preempt.h>
 #include <linux/lockdep.h>
 #include <linux/ftrace_irq.h>
 #include <linux/vtime.h>
 #include <asm/hardirq.h>
 
-
 extern void synchronize_irq(unsigned int irq);
 extern bool synchronize_hardirq(unsigned int irq);
 
-#if defined(CONFIG_TINY_RCU)
-
-static inline void rcu_nmi_enter(void)
-{
-}
+#ifdef CONFIG_NO_HZ_FULL
+void __rcu_irq_enter_check_tick(void);
+#else
+static inline void __rcu_irq_enter_check_tick(void) { }
+#endif
 
-static inline void rcu_nmi_exit(void)
+static __always_inline void rcu_irq_enter_check_tick(void)
 {
+       if (context_tracking_enabled())
+               __rcu_irq_enter_check_tick();
 }
 
-#else
-extern void rcu_nmi_enter(void);
-extern void rcu_nmi_exit(void);
-#endif
-
 /*
  * It is safe to do non-atomic ops on ->hardirq_context,
  * because NMI handlers may not preempt and the ops are
@@ -65,14 +62,34 @@ extern void irq_exit(void);
 #define arch_nmi_exit()                do { } while (0)
 #endif
 
+#ifdef CONFIG_TINY_RCU
+static inline void rcu_nmi_enter(void) { }
+static inline void rcu_nmi_exit(void) { }
+#else
+extern void rcu_nmi_enter(void);
+extern void rcu_nmi_exit(void);
+#endif
+
+/*
+ * NMI vs Tracing
+ * --------------
+ *
+ * We must not land in a tracer until (or after) we've changed preempt_count
+ * such that in_nmi() becomes true. To that effect all NMI C entry points must
+ * be marked 'notrace' and call nmi_enter() as soon as possible.
+ */
+
+/*
+ * nmi_enter() can nest up to 15 times; see NMI_BITS.
+ */
 #define nmi_enter()                                            \
        do {                                                    \
                arch_nmi_enter();                               \
                printk_nmi_enter();                             \
                lockdep_off();                                  \
                ftrace_nmi_enter();                             \
-               BUG_ON(in_nmi());                               \
-               preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET); \
+               BUG_ON(in_nmi() == NMI_MASK);                   \
+               __preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET);       \
                rcu_nmi_enter();                                \
                lockdep_hardirq_enter();                        \
        } while (0)
@@ -82,7 +99,7 @@ extern void irq_exit(void);
                lockdep_hardirq_exit();                         \
                rcu_nmi_exit();                                 \
                BUG_ON(!in_nmi());                              \
-               preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET); \
+               __preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);       \
                ftrace_nmi_exit();                              \
                lockdep_on();                                   \
                printk_nmi_exit();                              \
index 5e609f25878c0a6000d4207ce98740a5da99e562..363d4a814aa17a50ec702681302f85322dfa8875 100644 (file)
@@ -436,6 +436,9 @@ devm_hwmon_device_register_with_info(struct device *dev,
 void hwmon_device_unregister(struct device *dev);
 void devm_hwmon_device_unregister(struct device *dev);
 
+int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
+                      u32 attr, int channel);
+
 /**
  * hwmon_is_bad_char - Is the char invalid in a hwmon name
  * @ch: the char to be considered
index ac6e946b6767bba3d6750fafb374e364af1191b8..3ade03e5c7af3d3312bb43e6c11f718480c9172b 100644 (file)
@@ -171,7 +171,7 @@ static inline bool idr_is_empty(const struct idr *idr)
  */
 static inline void idr_preload_end(void)
 {
-       preempt_enable();
+       local_unlock(&radix_tree_preloads.lock);
 }
 
 /**
index 16268ef1cbccc4a4355062eeed0b329a94a0c8d8..5d3e48d020339b0e33d4edf3f974ca5273dcf983 100644 (file)
@@ -2047,7 +2047,7 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
 }
 
 /* HE Operation defines */
-#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK           0x00000003
+#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK           0x00000007
 #define IEEE80211_HE_OPERATION_TWT_REQUIRED                    0x00000008
 #define IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK              0x00003ff0
 #define IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET            4
index 79f918c6e8c5b94ef75c5e6f285bc969160c4e2c..906da5fc06e0f4aa66320cdf854fa05c5caf7ad6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * public include for LM8333 keypad driver - same license as driver
- * Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
+ * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
  */
 
 #ifndef _LM8333_H
index 8b09463dae0dba2a4a60ce2372863ba0e8fd16b9..bc20bd04c2a29626a96c882a5319a3e88ea9adfc 100644 (file)
@@ -155,8 +155,7 @@ loff_t iomap_apply(struct inode *inode, loff_t pos, loff_t length,
 ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from,
                const struct iomap_ops *ops);
 int iomap_readpage(struct page *page, const struct iomap_ops *ops);
-int iomap_readpages(struct address_space *mapping, struct list_head *pages,
-               unsigned nr_pages, const struct iomap_ops *ops);
+void iomap_readahead(struct readahead_control *, const struct iomap_ops *ops);
 int iomap_set_page_dirty(struct page *page);
 int iomap_is_partially_uptodate(struct page *page, unsigned long from,
                unsigned long count);
index 2e7a1e032c71a91e6a9dff98a0b637d621932a25..3378bcbe585eac4e76e93c768c287f6fd9c2eab8 100644 (file)
@@ -25,9 +25,8 @@ enum kmsg_dump_reason {
        KMSG_DUMP_PANIC,
        KMSG_DUMP_OOPS,
        KMSG_DUMP_EMERG,
-       KMSG_DUMP_RESTART,
-       KMSG_DUMP_HALT,
-       KMSG_DUMP_POWEROFF,
+       KMSG_DUMP_SHUTDOWN,
+       KMSG_DUMP_MAX
 };
 
 /**
@@ -71,6 +70,8 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper);
 int kmsg_dump_register(struct kmsg_dumper *dumper);
 
 int kmsg_dump_unregister(struct kmsg_dumper *dumper);
+
+const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason);
 #else
 static inline void kmsg_dump(enum kmsg_dump_reason reason)
 {
@@ -112,6 +113,11 @@ static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper)
 {
        return -EINVAL;
 }
+
+static inline const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
+{
+       return "Disabled";
+}
 #endif
 
 #endif /* _LINUX_KMSG_DUMP_H */
index e2ca0a292e2106d6367c0613e776fdcf1751fc7a..fc8d83e91379315b8afeeab40b8b6a6dc5901513 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2006-2008 Greg Kroah-Hartman <greg@kroah.com>
  * Copyright (c) 2006-2008 Novell Inc.
  *
- * Please read Documentation/kobject.txt before using the kobject
+ * Please read Documentation/core-api/kobject.rst before using the kobject
  * interface, ESPECIALLY the parts about reference counts and object
  * destructors.
  */
index 069aa2ebef90a4405fd32043a889c4b3cdf25c5b..2b5b64256cf4b1a3ccc834a7a0f6dd8a62513460 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Split from kobject.h by David Howells (dhowells@redhat.com)
  *
- * Please read Documentation/kobject.txt before using the kobject
+ * Please read Documentation/core-api/kobject.rst before using the kobject
  * interface, ESPECIALLY the parts about reference counts and object
  * destructors.
  */
diff --git a/include/linux/linear_range.h b/include/linux/linear_range.h
new file mode 100644 (file)
index 0000000..17b5943
--- /dev/null
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020 ROHM Semiconductors */
+
+#ifndef LINEAR_RANGE_H
+#define LINEAR_RANGE_H
+
+#include <linux/types.h>
+
+/**
+ * struct linear_range - table of selector - value pairs
+ *
+ * Define a lookup-table for range of values. Intended to help when looking
+ * for a register value matching certaing physical measure (like voltage).
+ * Usable when increment of one in register always results a constant increment
+ * of the physical measure (like voltage).
+ *
+ * @min:  Lowest value in range
+ * @min_sel: Lowest selector for range
+ * @max_sel: Highest selector for range
+ * @step: Value step size
+ */
+struct linear_range {
+       unsigned int min;
+       unsigned int min_sel;
+       unsigned int max_sel;
+       unsigned int step;
+};
+
+unsigned int linear_range_values_in_range(const struct linear_range *r);
+unsigned int linear_range_values_in_range_array(const struct linear_range *r,
+                                               int ranges);
+unsigned int linear_range_get_max_value(const struct linear_range *r);
+
+int linear_range_get_value(const struct linear_range *r, unsigned int selector,
+                          unsigned int *val);
+int linear_range_get_value_array(const struct linear_range *r, int ranges,
+                                unsigned int selector, unsigned int *val);
+int linear_range_get_selector_low(const struct linear_range *r,
+                                 unsigned int val, unsigned int *selector,
+                                 bool *found);
+int linear_range_get_selector_high(const struct linear_range *r,
+                                  unsigned int val, unsigned int *selector,
+                                  bool *found);
+int linear_range_get_selector_low_array(const struct linear_range *r,
+                                       int ranges, unsigned int val,
+                                       unsigned int *selector, bool *found);
+
+#endif
index 9280209d1f62c887d18c9de38f100a51c33b721d..d796ec20d1145ca6cdd4f311e567878fa220c5bf 100644 (file)
 
 /* === DEPRECATED annotations === */
 
-#ifndef CONFIG_X86
+#ifndef CONFIG_ARCH_USE_SYM_ANNOTATIONS
 #ifndef GLOBAL
 /* deprecated, use SYM_DATA*, SYM_ENTRY, or similar */
 #define GLOBAL(name) \
 #define ENTRY(name) \
        SYM_FUNC_START(name)
 #endif
-#endif /* CONFIG_X86 */
+#endif /* CONFIG_ARCH_USE_SYM_ANNOTATIONS */
 #endif /* LINKER_SCRIPT */
 
-#ifndef CONFIG_X86
+#ifndef CONFIG_ARCH_USE_SYM_ANNOTATIONS
 #ifndef WEAK
 /* deprecated, use SYM_FUNC_START_WEAK* */
 #define WEAK(name)        \
 #define ENDPROC(name) \
        SYM_FUNC_END(name)
 #endif
-#endif /* CONFIG_X86 */
+#endif /* CONFIG_ARCH_USE_SYM_ANNOTATIONS */
 
 /* === generic annotations === */
 
diff --git a/include/linux/local_lock.h b/include/linux/local_lock.h
new file mode 100644 (file)
index 0000000..e55010f
--- /dev/null
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_LOCAL_LOCK_H
+#define _LINUX_LOCAL_LOCK_H
+
+#include <linux/local_lock_internal.h>
+
+/**
+ * local_lock_init - Runtime initialize a lock instance
+ */
+#define local_lock_init(lock)          __local_lock_init(lock)
+
+/**
+ * local_lock - Acquire a per CPU local lock
+ * @lock:      The lock variable
+ */
+#define local_lock(lock)               __local_lock(lock)
+
+/**
+ * local_lock_irq - Acquire a per CPU local lock and disable interrupts
+ * @lock:      The lock variable
+ */
+#define local_lock_irq(lock)           __local_lock_irq(lock)
+
+/**
+ * local_lock_irqsave - Acquire a per CPU local lock, save and disable
+ *                      interrupts
+ * @lock:      The lock variable
+ * @flags:     Storage for interrupt flags
+ */
+#define local_lock_irqsave(lock, flags)                                \
+       __local_lock_irqsave(lock, flags)
+
+/**
+ * local_unlock - Release a per CPU local lock
+ * @lock:      The lock variable
+ */
+#define local_unlock(lock)             __local_unlock(lock)
+
+/**
+ * local_unlock_irq - Release a per CPU local lock and enable interrupts
+ * @lock:      The lock variable
+ */
+#define local_unlock_irq(lock)         __local_unlock_irq(lock)
+
+/**
+ * local_unlock_irqrestore - Release a per CPU local lock and restore
+ *                           interrupt flags
+ * @lock:      The lock variable
+ * @flags:      Interrupt flags to restore
+ */
+#define local_unlock_irqrestore(lock, flags)                   \
+       __local_unlock_irqrestore(lock, flags)
+
+#endif
diff --git a/include/linux/local_lock_internal.h b/include/linux/local_lock_internal.h
new file mode 100644 (file)
index 0000000..4a8795b
--- /dev/null
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_LOCAL_LOCK_H
+# error "Do not include directly, include linux/local_lock.h"
+#endif
+
+#include <linux/percpu-defs.h>
+#include <linux/lockdep.h>
+
+typedef struct {
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lockdep_map      dep_map;
+       struct task_struct      *owner;
+#endif
+} local_lock_t;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define LL_DEP_MAP_INIT(lockname)                     \
+       .dep_map = {                                    \
+               .name = #lockname,                      \
+               .wait_type_inner = LD_WAIT_CONFIG,      \
+       }
+#else
+# define LL_DEP_MAP_INIT(lockname)
+#endif
+
+#define INIT_LOCAL_LOCK(lockname)      { LL_DEP_MAP_INIT(lockname) }
+
+#define __local_lock_init(lock)                                        \
+do {                                                           \
+       static struct lock_class_key __key;                     \
+                                                               \
+       debug_check_no_locks_freed((void *)lock, sizeof(*lock));\
+       lockdep_init_map_wait(&(lock)->dep_map, #lock, &__key, 0, LD_WAIT_CONFIG);\
+} while (0)
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static inline void local_lock_acquire(local_lock_t *l)
+{
+       lock_map_acquire(&l->dep_map);
+       DEBUG_LOCKS_WARN_ON(l->owner);
+       l->owner = current;
+}
+
+static inline void local_lock_release(local_lock_t *l)
+{
+       DEBUG_LOCKS_WARN_ON(l->owner != current);
+       l->owner = NULL;
+       lock_map_release(&l->dep_map);
+}
+
+#else /* CONFIG_DEBUG_LOCK_ALLOC */
+static inline void local_lock_acquire(local_lock_t *l) { }
+static inline void local_lock_release(local_lock_t *l) { }
+#endif /* !CONFIG_DEBUG_LOCK_ALLOC */
+
+#define __local_lock(lock)                                     \
+       do {                                                    \
+               preempt_disable();                              \
+               local_lock_acquire(this_cpu_ptr(lock));         \
+       } while (0)
+
+#define __local_lock_irq(lock)                                 \
+       do {                                                    \
+               local_irq_disable();                            \
+               local_lock_acquire(this_cpu_ptr(lock));         \
+       } while (0)
+
+#define __local_lock_irqsave(lock, flags)                      \
+       do {                                                    \
+               local_irq_save(flags);                          \
+               local_lock_acquire(this_cpu_ptr(lock));         \
+       } while (0)
+
+#define __local_unlock(lock)                                   \
+       do {                                                    \
+               local_lock_release(this_cpu_ptr(lock));         \
+               preempt_enable();                               \
+       } while (0)
+
+#define __local_unlock_irq(lock)                               \
+       do {                                                    \
+               local_lock_release(this_cpu_ptr(lock));         \
+               local_irq_enable();                             \
+       } while (0)
+
+#define __local_unlock_irqrestore(lock, flags)                 \
+       do {                                                    \
+               local_lock_release(this_cpu_ptr(lock));         \
+               local_irq_restore(flags);                       \
+       } while (0)
index 206774ac69460af889af852598a5f14fad651a21..8fce5c98a4b0ecadee49f08a1d3addae1254a08c 100644 (file)
@@ -308,8 +308,27 @@ extern void lockdep_set_selftest_task(struct task_struct *task);
 
 extern void lockdep_init_task(struct task_struct *task);
 
-extern void lockdep_off(void);
-extern void lockdep_on(void);
+/*
+ * Split the recrursion counter in two to readily detect 'off' vs recursion.
+ */
+#define LOCKDEP_RECURSION_BITS 16
+#define LOCKDEP_OFF            (1U << LOCKDEP_RECURSION_BITS)
+#define LOCKDEP_RECURSION_MASK (LOCKDEP_OFF - 1)
+
+/*
+ * lockdep_{off,on}() are macros to avoid tracing and kprobes; not inlines due
+ * to header dependencies.
+ */
+
+#define lockdep_off()                                  \
+do {                                                   \
+       current->lockdep_recursion += LOCKDEP_OFF;      \
+} while (0)
+
+#define lockdep_on()                                   \
+do {                                                   \
+       current->lockdep_recursion -= LOCKDEP_OFF;      \
+} while (0)
 
 extern void lockdep_register_key(struct lock_class_key *key);
 extern void lockdep_unregister_key(struct lock_class_key *key);
index 988ca0df7824c52dbc88c8b664c770f588d0f854..44d5422c18e4bd2fe96e1a36cc21d7f0d556367f 100644 (file)
@@ -77,7 +77,7 @@
  *     state.  This is called immediately after commit_creds().
  *
  * Security hooks for mount using fs_context.
- *     [See also Documentation/filesystems/mount_api.txt]
+ *     [See also Documentation/filesystems/mount_api.rst]
  *
  * @fs_context_dup:
  *     Allocate and attach a security structure to sc->security.  This pointer
index 977edd3b7bd8934b9408b81ff5591a9a684b09f9..bfe9533bb67ee1e01a7782b87096f9aaa7aac6b8 100644 (file)
@@ -45,6 +45,7 @@ enum memcg_memory_event {
        MEMCG_MAX,
        MEMCG_OOM,
        MEMCG_OOM_KILL,
+       MEMCG_SWAP_HIGH,
        MEMCG_SWAP_MAX,
        MEMCG_SWAP_FAIL,
        MEMCG_NR_MEMORY_EVENTS,
@@ -215,9 +216,6 @@ struct mem_cgroup {
        struct page_counter kmem;
        struct page_counter tcpmem;
 
-       /* Upper bound of normal memory consumption range */
-       unsigned long high;
-
        /* Range enforcement for interrupt charges */
        struct work_struct high_work;
 
index 216a713bef7f0e001a11cc480ecb3d72ea990ff7..da4c65f9435ff13a4addce29dd5a9eb13f46b370 100644 (file)
@@ -288,7 +288,7 @@ struct memstick_host {
        int                 (*set_param)(struct memstick_host *host,
                                         enum memstick_param param,
                                         int value);
-       unsigned long       private[0] ____cacheline_aligned;
+       unsigned long       private[] ____cacheline_aligned;
 };
 
 struct memstick_driver {
diff --git a/include/linux/mfd/gsc.h b/include/linux/mfd/gsc.h
new file mode 100644 (file)
index 0000000..6bd639c
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Gateworks Corporation
+ */
+#ifndef __LINUX_MFD_GSC_H_
+#define __LINUX_MFD_GSC_H_
+
+#include <linux/regmap.h>
+
+/* Device Addresses */
+#define GSC_MISC       0x20
+#define GSC_UPDATE     0x21
+#define GSC_GPIO       0x23
+#define GSC_HWMON      0x29
+#define GSC_EEPROM0    0x50
+#define GSC_EEPROM1    0x51
+#define GSC_EEPROM2    0x52
+#define GSC_EEPROM3    0x53
+#define GSC_RTC                0x68
+
+/* Register offsets */
+enum {
+       GSC_CTRL_0      = 0x00,
+       GSC_CTRL_1      = 0x01,
+       GSC_TIME        = 0x02,
+       GSC_TIME_ADD    = 0x06,
+       GSC_IRQ_STATUS  = 0x0A,
+       GSC_IRQ_ENABLE  = 0x0B,
+       GSC_FW_CRC      = 0x0C,
+       GSC_FW_VER      = 0x0E,
+       GSC_WP          = 0x0F,
+};
+
+/* Bit definitions */
+#define GSC_CTRL_0_PB_HARD_RESET       0
+#define GSC_CTRL_0_PB_CLEAR_SECURE_KEY 1
+#define GSC_CTRL_0_PB_SOFT_POWER_DOWN  2
+#define GSC_CTRL_0_PB_BOOT_ALTERNATE   3
+#define GSC_CTRL_0_PERFORM_CRC         4
+#define GSC_CTRL_0_TAMPER_DETECT       5
+#define GSC_CTRL_0_SWITCH_HOLD         6
+
+#define GSC_CTRL_1_SLEEP_ENABLE                0
+#define GSC_CTRL_1_SLEEP_ACTIVATE      1
+#define GSC_CTRL_1_SLEEP_ADD           2
+#define GSC_CTRL_1_SLEEP_NOWAKEPB      3
+#define GSC_CTRL_1_WDT_TIME            4
+#define GSC_CTRL_1_WDT_ENABLE          5
+#define GSC_CTRL_1_SWITCH_BOOT_ENABLE  6
+#define GSC_CTRL_1_SWITCH_BOOT_CLEAR   7
+
+#define GSC_IRQ_PB                     0
+#define GSC_IRQ_KEY_ERASED             1
+#define GSC_IRQ_EEPROM_WP              2
+#define GSC_IRQ_RESV                   3
+#define GSC_IRQ_GPIO                   4
+#define GSC_IRQ_TAMPER                 5
+#define GSC_IRQ_WDT_TIMEOUT            6
+#define GSC_IRQ_SWITCH_HOLD            7
+
+int gsc_read(void *context, unsigned int reg, unsigned int *val);
+int gsc_write(void *context, unsigned int reg, unsigned int val);
+
+struct gsc_dev {
+       struct device *dev;
+
+       struct i2c_client *i2c;         /* 0x20: interrupt controller, WDT */
+       struct i2c_client *i2c_hwmon;   /* 0x29: hwmon, fan controller */
+
+       struct regmap *regmap;
+
+       unsigned int fwver;
+       unsigned short fwcrc;
+};
+
+#endif /* __LINUX_MFD_GSC_H_ */
diff --git a/include/linux/mfd/intel_pmc_bxt.h b/include/linux/mfd/intel_pmc_bxt.h
new file mode 100644 (file)
index 0000000..f51a43d
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef MFD_INTEL_PMC_BXT_H
+#define MFD_INTEL_PMC_BXT_H
+
+/* GCR reg offsets from GCR base */
+#define PMC_GCR_PMC_CFG_REG            0x08
+#define PMC_GCR_TELEM_DEEP_S0IX_REG    0x78
+#define PMC_GCR_TELEM_SHLW_S0IX_REG    0x80
+
+/* PMC_CFG_REG bit masks */
+#define PMC_CFG_NO_REBOOT_EN           BIT(4)
+
+/**
+ * struct intel_pmc_dev - Intel PMC device structure
+ * @dev: Pointer to the parent PMC device
+ * @scu: Pointer to the SCU IPC device data structure
+ * @gcr_mem_base: Virtual base address of GCR (Global Configuration Registers)
+ * @gcr_lock: Lock used to serialize access to GCR registers
+ * @telem_base: Pointer to telemetry SSRAM base resource or %NULL if not
+ *             available
+ */
+struct intel_pmc_dev {
+       struct device *dev;
+       struct intel_scu_ipc_dev *scu;
+       void __iomem *gcr_mem_base;
+       spinlock_t gcr_lock;
+       struct resource *telem_base;
+};
+
+#if IS_ENABLED(CONFIG_MFD_INTEL_PMC_BXT)
+int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
+int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val);
+int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
+#else
+static inline int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset,
+                                      u64 *data)
+{
+       return -ENOTSUPP;
+}
+
+static inline int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset,
+                                      u32 mask, u32 val)
+{
+       return -ENOTSUPP;
+}
+
+static inline int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
+{
+       return -ENOTSUPP;
+}
+#endif
+
+#endif /* MFD_INTEL_PMC_BXT_H */
index bfecd6bd499057e29962aefa4ec74a8ff2f3273c..6a88e34cb955336bc8f8b439c3e4ec2638b75202 100644 (file)
 
 #include <linux/regmap.h>
 
+/**
+ * struct intel_soc_pmic - Intel SoC PMIC data
+ * @irq: Master interrupt number of the parent PMIC device
+ * @regmap: Pointer to the parent PMIC device regmap structure
+ * @irq_chip_data: IRQ chip data for the PMIC itself
+ * @irq_chip_data_pwrbtn: Chained IRQ chip data for the Power Button
+ * @irq_chip_data_tmu: Chained IRQ chip data for the Time Management Unit
+ * @irq_chip_data_bcu: Chained IRQ chip data for the Burst Control Unit
+ * @irq_chip_data_adc: Chained IRQ chip data for the General Purpose ADC
+ * @irq_chip_data_chgr: Chained IRQ chip data for the External Charger
+ * @irq_chip_data_crit: Chained IRQ chip data for the Critical Event Handler
+ * @dev: Pointer to the parent PMIC device
+ * @scu: Pointer to the SCU IPC device data structure
+ */
 struct intel_soc_pmic {
        int irq;
        struct regmap *regmap;
@@ -24,6 +38,7 @@ struct intel_soc_pmic {
        struct regmap_irq_chip_data *irq_chip_data_chgr;
        struct regmap_irq_chip_data *irq_chip_data_crit;
        struct device *dev;
+       struct intel_scu_ipc_dev *scu;
 };
 
 int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
index 061af220dcd3a5c9621679838bfff5a48210e701..79c020bd0c70ea72ca47c88340ebd6ac4b509e3f 100644 (file)
@@ -39,6 +39,7 @@ enum {
        MAX8998_ENVICHG,
        MAX8998_ESAFEOUT1,
        MAX8998_ESAFEOUT2,
+       MAX8998_CHARGER,
 };
 
 /**
index c606dbbfa5e15a19433d93a4e4e511cd4b9428b6..7e07f4f490cbc3f2e9e5809173d2b920d3026bc6 100644 (file)
@@ -325,17 +325,13 @@ extern unsigned int kobjsize(const void *objp);
 #elif defined(CONFIG_SPARC64)
 # define VM_SPARC_ADI  VM_ARCH_1       /* Uses ADI tag for access control */
 # define VM_ARCH_CLEAR VM_SPARC_ADI
+#elif defined(CONFIG_ARM64)
+# define VM_ARM64_BTI  VM_ARCH_1       /* BTI guarded page, a.k.a. GP bit */
+# define VM_ARCH_CLEAR VM_ARM64_BTI
 #elif !defined(CONFIG_MMU)
 # define VM_MAPPED_COPY        VM_ARCH_1       /* T if mapped copy of data (nommu mmap) */
 #endif
 
-#if defined(CONFIG_X86_INTEL_MPX)
-/* MPX specific bounds table or bounds directory */
-# define VM_MPX                VM_HIGH_ARCH_4
-#else
-# define VM_MPX                VM_NONE
-#endif
-
 #ifndef VM_GROWSUP
 # define VM_GROWSUP    VM_NONE
 #endif
@@ -782,6 +778,11 @@ static inline void *kvcalloc(size_t n, size_t size, gfp_t flags)
 
 extern void kvfree(const void *addr);
 
+/*
+ * Mapcount of compound page as a whole, does not include mapped sub-pages.
+ *
+ * Must be called only for compound pages or any their tail sub-pages.
+ */
 static inline int compound_mapcount(struct page *page)
 {
        VM_BUG_ON_PAGE(!PageCompound(page), page);
@@ -801,10 +802,16 @@ static inline void page_mapcount_reset(struct page *page)
 
 int __page_mapcount(struct page *page);
 
+/*
+ * Mapcount of 0-order page; when compound sub-page, includes
+ * compound_mapcount().
+ *
+ * Result is undefined for pages which cannot be mapped into userspace.
+ * For example SLAB or special types of pages. See function page_has_type().
+ * They use this place in struct page differently.
+ */
 static inline int page_mapcount(struct page *page)
 {
-       VM_BUG_ON_PAGE(PageSlab(page), page);
-
        if (unlikely(PageCompound(page)))
                return __page_mapcount(page);
        return atomic_read(&page->_mapcount) + 1;
@@ -1219,7 +1226,7 @@ void unpin_user_pages(struct page **pages, unsigned long npages);
  * used to track the pincount (instead using of the GUP_PIN_COUNTING_BIAS
  * scheme).
  *
- * For more information, please see Documentation/vm/pin_user_pages.rst.
+ * For more information, please see Documentation/core-api/pin_user_pages.rst.
  *
  * @page:      pointer to page to be queried.
  * @Return:    True, if it is likely that the page has been "dma-pinned".
@@ -1702,6 +1709,8 @@ long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
                    unsigned int gup_flags, struct page **pages, int *locked);
 long get_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
                    struct page **pages, unsigned int gup_flags);
+long pin_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
+                   struct page **pages, unsigned int gup_flags);
 
 int get_user_pages_fast(unsigned long start, int nr_pages,
                        unsigned int gup_flags, struct page **pages);
@@ -2078,13 +2087,54 @@ static inline pud_t *pud_alloc(struct mm_struct *mm, p4d_t *p4d,
        return (unlikely(p4d_none(*p4d)) && __pud_alloc(mm, p4d, address)) ?
                NULL : pud_offset(p4d, address);
 }
+
+static inline p4d_t *p4d_alloc_track(struct mm_struct *mm, pgd_t *pgd,
+                                    unsigned long address,
+                                    pgtbl_mod_mask *mod_mask)
+
+{
+       if (unlikely(pgd_none(*pgd))) {
+               if (__p4d_alloc(mm, pgd, address))
+                       return NULL;
+               *mod_mask |= PGTBL_PGD_MODIFIED;
+       }
+
+       return p4d_offset(pgd, address);
+}
+
 #endif /* !__ARCH_HAS_5LEVEL_HACK */
 
+static inline pud_t *pud_alloc_track(struct mm_struct *mm, p4d_t *p4d,
+                                    unsigned long address,
+                                    pgtbl_mod_mask *mod_mask)
+{
+       if (unlikely(p4d_none(*p4d))) {
+               if (__pud_alloc(mm, p4d, address))
+                       return NULL;
+               *mod_mask |= PGTBL_P4D_MODIFIED;
+       }
+
+       return pud_offset(p4d, address);
+}
+
 static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
 {
        return (unlikely(pud_none(*pud)) && __pmd_alloc(mm, pud, address))?
                NULL: pmd_offset(pud, address);
 }
+
+static inline pmd_t *pmd_alloc_track(struct mm_struct *mm, pud_t *pud,
+                                    unsigned long address,
+                                    pgtbl_mod_mask *mod_mask)
+{
+       if (unlikely(pud_none(*pud))) {
+               if (__pmd_alloc(mm, pud, address))
+                       return NULL;
+               *mod_mask |= PGTBL_PUD_MODIFIED;
+       }
+
+       return pmd_offset(pud, address);
+}
 #endif /* CONFIG_MMU */
 
 #if USE_SPLIT_PTE_PTLOCKS
@@ -2200,6 +2250,11 @@ static inline void pgtable_pte_page_dtor(struct page *page)
        ((unlikely(pmd_none(*(pmd))) && __pte_alloc_kernel(pmd))? \
                NULL: pte_offset_kernel(pmd, address))
 
+#define pte_alloc_kernel_track(pmd, address, mask)                     \
+       ((unlikely(pmd_none(*(pmd))) &&                                 \
+         (__pte_alloc_kernel(pmd) || ({*(mask)|=PGTBL_PMD_MODIFIED;0;})))?\
+               NULL: pte_offset_kernel(pmd, address))
+
 #if USE_SPLIT_PMD_PTLOCKS
 
 static struct page *pmd_to_page(pmd_t *pmd)
@@ -2601,25 +2656,6 @@ extern vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf);
 int __must_check write_one_page(struct page *page);
 void task_dirty_inc(struct task_struct *tsk);
 
-/* readahead.c */
-#define VM_READAHEAD_PAGES     (SZ_128K / PAGE_SIZE)
-
-int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
-                       pgoff_t offset, unsigned long nr_to_read);
-
-void page_cache_sync_readahead(struct address_space *mapping,
-                              struct file_ra_state *ra,
-                              struct file *filp,
-                              pgoff_t offset,
-                              unsigned long size);
-
-void page_cache_async_readahead(struct address_space *mapping,
-                               struct file_ra_state *ra,
-                               struct file *filp,
-                               struct page *pg,
-                               pgoff_t offset,
-                               unsigned long size);
-
 extern unsigned long stack_guard_gap;
 /* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */
 extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
@@ -2834,7 +2870,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address,
  * releasing pages: get_user_pages*() pages must be released via put_page(),
  * while pin_user_pages*() pages must be released via unpin_user_page().
  *
- * Please see Documentation/vm/pin_user_pages.rst for more information.
+ * Please see Documentation/core-api/pin_user_pages.rst for more information.
  */
 
 static inline int vm_fault_to_errno(vm_fault_t vm_fault, int foll_flags)
index 4aba6c0c2ba80a8101f9db201ff977aa7cdf3065..ef6d3aface8a29d72ca45a9a32c64551f09c88d5 100644 (file)
@@ -240,7 +240,11 @@ static inline atomic_t *compound_pincount_ptr(struct page *page)
 #define PAGE_FRAG_CACHE_MAX_ORDER      get_order(PAGE_FRAG_CACHE_MAX_SIZE)
 
 #define page_private(page)             ((page)->private)
-#define set_page_private(page, v)      ((page)->private = (v))
+
+static inline void set_page_private(struct page *page, unsigned long private)
+{
+       page->private = private;
+}
 
 struct page_frag_cache {
        void * va;
index cf3780a6ccc4bc210bccbabb857f8b4370e3f146..7d46411ffaa21477831d8f3352e871a353ae4b57 100644 (file)
@@ -48,6 +48,7 @@ struct mmc_ext_csd {
        u8                      sec_feature_support;
        u8                      rel_sectors;
        u8                      rel_param;
+       bool                    enhanced_rpmb_supported;
        u8                      part_config;
        u8                      cache_ctrl;
        u8                      rst_n_function;
index c318fb5b6a94180926b49bc18920076289226ee5..7149bab555d7123d25754fc3e52e0c7bddf68191 100644 (file)
@@ -92,6 +92,9 @@ struct mmc_host_ops {
                            int err);
        void    (*pre_req)(struct mmc_host *host, struct mmc_request *req);
        void    (*request)(struct mmc_host *host, struct mmc_request *req);
+       /* Submit one request to host in atomic context. */
+       int     (*request_atomic)(struct mmc_host *host,
+                                 struct mmc_request *req);
 
        /*
         * Avoid calling the next three functions too often or in a "fast
@@ -318,7 +321,6 @@ struct mmc_host {
 #define MMC_CAP_AGGRESSIVE_PM  (1 << 7)        /* Suspend (e)MMC/SD at idle  */
 #define MMC_CAP_NONREMOVABLE   (1 << 8)        /* Nonremovable e.g. eMMC */
 #define MMC_CAP_WAIT_WHILE_BUSY        (1 << 9)        /* Waits while card is busy */
-#define MMC_CAP_ERASE          (1 << 10)       /* Allow erase/trim commands */
 #define MMC_CAP_3_3V_DDR       (1 << 11)       /* Host supports eMMC DDR 3.3V */
 #define MMC_CAP_1_8V_DDR       (1 << 12)       /* Host supports eMMC DDR 1.8V */
 #define MMC_CAP_1_2V_DDR       (1 << 13)       /* Host supports eMMC DDR 1.2V */
index 4b85ef05a9066a09b7a59f0a981a653fed59718f..d9a65c6a8816fdacbfba6a364ea4076d1cc72bad 100644 (file)
@@ -325,6 +325,7 @@ static inline bool mmc_ready_for_data(u32 status)
  */
 
 #define EXT_CSD_WR_REL_PARAM_EN                (1<<2)
+#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR    (1<<4)
 
 #define EXT_CSD_BOOT_WP_B_PWR_WP_DIS   (0x40)
 #define EXT_CSD_BOOT_WP_B_PERM_WP_DIS  (0x10)
index 2e9a6e4634eb3a2042508884da6f429f6c631edc..15ed8ce9d3943f9a0e9aed04f0d1214f63103ce5 100644 (file)
 /*
  * Vendors and devices.  Sort key: vendor first, device next.
  */
+
+#define SDIO_VENDOR_ID_STE                     0x0020
+#define SDIO_DEVICE_ID_STE_CW1200              0x2280
+
+#define SDIO_VENDOR_ID_INTEL                   0x0089
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX     0x1402
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI      0x1403
+#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP       0x1404
+#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS       0x1405
+#define SDIO_DEVICE_ID_INTEL_IWMC3200BT                0x1406
+#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
+
+#define SDIO_VENDOR_ID_CGUYS                   0x0092
+#define SDIO_DEVICE_ID_CGUYS_EW_CG1102GC       0x0004
+
+#define SDIO_VENDOR_ID_TI                      0x0097
+#define SDIO_DEVICE_ID_TI_WL1271               0x4076
+
+#define SDIO_VENDOR_ID_ATHEROS                 0x0271
+#define SDIO_DEVICE_ID_ATHEROS_AR6003_00       0x0300
+#define SDIO_DEVICE_ID_ATHEROS_AR6003_01       0x0301
+#define SDIO_DEVICE_ID_ATHEROS_AR6004_00       0x0400
+#define SDIO_DEVICE_ID_ATHEROS_AR6004_01       0x0401
+#define SDIO_DEVICE_ID_ATHEROS_AR6004_02       0x0402
+#define SDIO_DEVICE_ID_ATHEROS_AR6004_18       0x0418
+#define SDIO_DEVICE_ID_ATHEROS_AR6004_19       0x0419
+#define SDIO_DEVICE_ID_ATHEROS_AR6005          0x050A
+#define SDIO_DEVICE_ID_ATHEROS_QCA9377         0x0701
+
 #define SDIO_VENDOR_ID_BROADCOM                        0x02d0
-#define SDIO_DEVICE_ID_BROADCOM_43143          0xa887
+#define SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII   0x044b
 #define SDIO_DEVICE_ID_BROADCOM_43241          0x4324
 #define SDIO_DEVICE_ID_BROADCOM_4329           0x4329
 #define SDIO_DEVICE_ID_BROADCOM_4330           0x4330
 #define SDIO_DEVICE_ID_BROADCOM_4334           0x4334
-#define SDIO_DEVICE_ID_BROADCOM_43340          0xa94c
-#define SDIO_DEVICE_ID_BROADCOM_43341          0xa94d
 #define SDIO_DEVICE_ID_BROADCOM_4335_4339      0x4335
 #define SDIO_DEVICE_ID_BROADCOM_4339           0x4339
-#define SDIO_DEVICE_ID_BROADCOM_43362          0xa962
-#define SDIO_DEVICE_ID_BROADCOM_43364          0xa9a4
-#define SDIO_DEVICE_ID_BROADCOM_43430          0xa9a6
 #define SDIO_DEVICE_ID_BROADCOM_4345           0x4345
-#define SDIO_DEVICE_ID_BROADCOM_43455          0xa9bf
 #define SDIO_DEVICE_ID_BROADCOM_4354           0x4354
+#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359  0x4355
 #define SDIO_DEVICE_ID_BROADCOM_4356           0x4356
 #define SDIO_DEVICE_ID_BROADCOM_4359           0x4359
-#define SDIO_DEVICE_ID_CYPRESS_4373            0x4373
-#define SDIO_DEVICE_ID_CYPRESS_43012           43012
-#define SDIO_DEVICE_ID_CYPRESS_89359           0x4355
-
-#define SDIO_VENDOR_ID_INTEL                   0x0089
-#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX     0x1402
-#define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI      0x1403
-#define SDIO_DEVICE_ID_INTEL_IWMC3200TOP       0x1404
-#define SDIO_DEVICE_ID_INTEL_IWMC3200GPS       0x1405
-#define SDIO_DEVICE_ID_INTEL_IWMC3200BT                0x1406
-#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
+#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373   0x4373
+#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012  0xa804
+#define SDIO_DEVICE_ID_BROADCOM_43143          0xa887
+#define SDIO_DEVICE_ID_BROADCOM_43340          0xa94c
+#define SDIO_DEVICE_ID_BROADCOM_43341          0xa94d
+#define SDIO_DEVICE_ID_BROADCOM_43362          0xa962
+#define SDIO_DEVICE_ID_BROADCOM_43364          0xa9a4
+#define SDIO_DEVICE_ID_BROADCOM_43430          0xa9a6
+#define SDIO_DEVICE_ID_BROADCOM_43455          0xa9bf
 
 #define SDIO_VENDOR_ID_MARVELL                 0x02df
 #define SDIO_DEVICE_ID_MARVELL_LIBERTAS                0x9103
-#define SDIO_DEVICE_ID_MARVELL_8688WLAN                0x9104
-#define SDIO_DEVICE_ID_MARVELL_8688BT          0x9105
+#define SDIO_DEVICE_ID_MARVELL_8688_WLAN       0x9104
+#define SDIO_DEVICE_ID_MARVELL_8688_BT         0x9105
+#define SDIO_DEVICE_ID_MARVELL_8786_WLAN       0x9116
+#define SDIO_DEVICE_ID_MARVELL_8787_WLAN       0x9119
+#define SDIO_DEVICE_ID_MARVELL_8787_BT         0x911a
+#define SDIO_DEVICE_ID_MARVELL_8787_BT_AMP     0x911b
 #define SDIO_DEVICE_ID_MARVELL_8797_F0         0x9128
-#define SDIO_DEVICE_ID_MARVELL_8887WLAN        0x9134
+#define SDIO_DEVICE_ID_MARVELL_8797_WLAN       0x9129
+#define SDIO_DEVICE_ID_MARVELL_8797_BT         0x912a
+#define SDIO_DEVICE_ID_MARVELL_8897_WLAN       0x912d
+#define SDIO_DEVICE_ID_MARVELL_8897_BT         0x912e
+#define SDIO_DEVICE_ID_MARVELL_8887_F0         0x9134
+#define SDIO_DEVICE_ID_MARVELL_8887_WLAN       0x9135
+#define SDIO_DEVICE_ID_MARVELL_8887_BT         0x9136
+#define SDIO_DEVICE_ID_MARVELL_8801_WLAN       0x9139
+#define SDIO_DEVICE_ID_MARVELL_8997_F0         0x9140
+#define SDIO_DEVICE_ID_MARVELL_8997_WLAN       0x9141
+#define SDIO_DEVICE_ID_MARVELL_8997_BT         0x9142
+#define SDIO_DEVICE_ID_MARVELL_8977_WLAN       0x9145
+#define SDIO_DEVICE_ID_MARVELL_8977_BT         0x9146
+#define SDIO_DEVICE_ID_MARVELL_8987_WLAN       0x9149
+#define SDIO_DEVICE_ID_MARVELL_8987_BT         0x914a
 
 #define SDIO_VENDOR_ID_MEDIATEK                        0x037a
+#define SDIO_DEVICE_ID_MEDIATEK_MT7663         0x7663
+#define SDIO_DEVICE_ID_MEDIATEK_MT7668         0x7668
 
 #define SDIO_VENDOR_ID_SIANO                   0x039a
 #define SDIO_DEVICE_ID_SIANO_NOVA_B0           0x0201
 #define SDIO_DEVICE_ID_SIANO_NICE              0x0202
 #define SDIO_DEVICE_ID_SIANO_VEGA_A0           0x0300
 #define SDIO_DEVICE_ID_SIANO_VENICE            0x0301
+#define SDIO_DEVICE_ID_SIANO_MING              0x0302
+#define SDIO_DEVICE_ID_SIANO_PELE              0x0500
+#define SDIO_DEVICE_ID_SIANO_RIO               0x0600
+#define SDIO_DEVICE_ID_SIANO_DENVER_2160       0x0700
+#define SDIO_DEVICE_ID_SIANO_DENVER_1530       0x0800
 #define SDIO_DEVICE_ID_SIANO_NOVA_A0           0x1100
 #define SDIO_DEVICE_ID_SIANO_STELLAR           0x5347
 
-#define SDIO_VENDOR_ID_TI                      0x0097
-#define SDIO_DEVICE_ID_TI_WL1271               0x4076
 #define SDIO_VENDOR_ID_TI_WL1251               0x104c
 #define SDIO_DEVICE_ID_TI_WL1251               0x9066
 
-#define SDIO_VENDOR_ID_STE                     0x0020
-#define SDIO_DEVICE_ID_STE_CW1200              0x2280
-
 #endif /* LINUX_MMC_SDIO_IDS_H */
index 1b9de7d220fb7856b71fc54510bdabbfc947d096..fdd9beb5efedd5c95a5d9f8ff6d13e6eb984b354 100644 (file)
@@ -156,6 +156,9 @@ enum zone_stat_item {
        NR_MLOCK,               /* mlock()ed pages found and moved off LRU */
        NR_PAGETABLE,           /* used for pagetables */
        NR_KERNEL_STACK_KB,     /* measured in KiB */
+#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
+       NR_KERNEL_SCS_KB,       /* measured in KiB */
+#endif
        /* Second 128 byte cacheline */
        NR_BOUNCE,
 #if IS_ENABLED(CONFIG_ZSMALLOC)
@@ -193,7 +196,6 @@ enum node_stat_item {
        NR_FILE_THPS,
        NR_FILE_PMDMAPPED,
        NR_ANON_THPS,
-       NR_UNSTABLE_NFS,        /* NFS unstable pages */
        NR_VMSCAN_WRITE,
        NR_VMSCAN_IMMEDIATE,    /* Prioritise for reclaim when writeback ends */
        NR_DIRTIED,             /* page dirtyings since bootup */
index 4c2ddd0941a7514e3b30adbb3211db90c9c39aab..0754b8d71262bbcd284f8ba410d954f19fb73ff5 100644 (file)
@@ -663,6 +663,7 @@ struct x86_cpu_id {
        __u16 vendor;
        __u16 family;
        __u16 model;
+       __u16 steppings;
        __u16 feature;  /* bit index */
        kernel_ulong_t driver_data;
 };
@@ -671,6 +672,7 @@ struct x86_cpu_id {
 #define X86_VENDOR_ANY 0xffff
 #define X86_FAMILY_ANY 0
 #define X86_MODEL_ANY  0
+#define X86_STEPPING_ANY 0
 #define X86_FEATURE_ANY 0      /* Same as FPU, you can't test for that */
 
 /*
index 1ad393e62bef6d1e32016fa9a7bc3a2a669e68c9..d849d06e4d44ffdba09475a2538f22f1144b8ae8 100644 (file)
@@ -458,6 +458,8 @@ struct module {
        void __percpu *percpu;
        unsigned int percpu_size;
 #endif
+       void *noinstr_text_start;
+       unsigned int noinstr_text_size;
 
 #ifdef CONFIG_TRACEPOINTS
        unsigned int num_tracepoints;
@@ -489,6 +491,12 @@ struct module {
        unsigned int num_ftrace_callsites;
        unsigned long *ftrace_callsites;
 #endif
+#ifdef CONFIG_KPROBES
+       void *kprobes_text_start;
+       unsigned int kprobes_text_size;
+       unsigned long *kprobe_blacklist;
+       unsigned int num_kprobe_blacklist;
+#endif
 
 #ifdef CONFIG_LIVEPATCH
        bool klp; /* Is this a livepatch module? */
index ca92aea8a6bd4f3efe73cce66f364021f92225cc..4fa67a8b22652e30737fd3158d73ac84da426b65 100644 (file)
@@ -29,6 +29,11 @@ void *module_alloc(unsigned long size);
 /* Free memory returned from module_alloc. */
 void module_memfree(void *module_region);
 
+/* Determines if the section name is an init section (that is only used during
+ * module loading).
+ */
+bool module_init_section(const char *name);
+
 /* Determines if the section name is an exit section (that is only used during
  * module unloading)
  */
index bf8cc4108b8f9ce103770b3f1aeb147de3f23c7c..7edac8c7a9c1c9388d534e8a9b390156641eda31 100644 (file)
@@ -50,7 +50,8 @@ struct fs_context;
 #define MNT_ATIME_MASK (MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME )
 
 #define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \
-                           MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED)
+                           MNT_DOOMED | MNT_SYNC_UMOUNT | MNT_MARKED | \
+                           MNT_CURSOR)
 
 #define MNT_INTERNAL   0x4000
 
@@ -64,6 +65,7 @@ struct fs_context;
 #define MNT_SYNC_UMOUNT                0x2000000
 #define MNT_MARKED             0x4000000
 #define MNT_UMOUNT             0x8000000
+#define MNT_CURSOR             0x10000000
 
 struct vfsmount {
        struct dentry *mnt_root;        /* root of the mounted tree */
index 001f1fcf98361db20ccb2e09dfa36a6ffb78ace1..f4f5e90a6844151597506cfc81d87ec0846fe71d 100644 (file)
@@ -13,9 +13,9 @@
 #ifdef CONFIG_BLOCK
 
 struct writeback_control;
+struct readahead_control;
 
-int mpage_readpages(struct address_space *mapping, struct list_head *pages,
-                               unsigned nr_pages, get_block_t get_block);
+void mpage_readahead(struct readahead_control *, get_block_t get_block);
 int mpage_readpage(struct page *page, get_block_t get_block);
 int mpage_writepages(struct address_space *mapping,
                struct writeback_control *wbc, get_block_t get_block);
index fcc409de31a406af2c8418e29077f0f0abe68699..a28aa289afdca9bd5653764cfbc38f784e1c82ed 100644 (file)
@@ -10,7 +10,7 @@
 #include <net/netfilter/nf_conntrack_expect.h>
 #include <uapi/linux/netfilter/nf_conntrack_tuple_common.h>
 
-extern const char *const pptp_msg_name[];
+const char *pptp_msg_name(u_int16_t msg);
 
 /* state of the control session */
 enum pptp_ctrlsess_state {
index a0d8b41850b2536b60d060361b2db73dfc33a15b..693cae9bfe66cab0cd63eea2432a8b5617b7920e 100644 (file)
@@ -139,7 +139,8 @@ struct padata_shell {
 /**
  * struct padata_instance - The overall control structure.
  *
- * @node: Used by CPU hotplug.
+ * @cpu_online_node: Linkage for CPU online callback.
+ * @cpu_dead_node: Linkage for CPU offline callback.
  * @parallel_wq: The workqueue used for parallel work.
  * @serial_wq: The workqueue used for serial work.
  * @pslist: List of padata_shell objects attached to this instance.
@@ -150,7 +151,8 @@ struct padata_shell {
  * @flags: padata flags.
  */
 struct padata_instance {
-       struct hlist_node                node;
+       struct hlist_node               cpu_online_node;
+       struct hlist_node               cpu_dead_node;
        struct workqueue_struct         *parallel_wq;
        struct workqueue_struct         *serial_wq;
        struct list_head                pslist;
index bab7e57f659b4c03d75548c32edcb12dcab86911..85bd413e784e88f3ff1a958d3a743dad1ecacd7b 100644 (file)
@@ -10,6 +10,7 @@ struct page_counter {
        atomic_long_t usage;
        unsigned long min;
        unsigned long low;
+       unsigned long high;
        unsigned long max;
        struct page_counter *parent;
 
@@ -55,6 +56,13 @@ bool page_counter_try_charge(struct page_counter *counter,
 void page_counter_uncharge(struct page_counter *counter, unsigned long nr_pages);
 void page_counter_set_min(struct page_counter *counter, unsigned long nr_pages);
 void page_counter_set_low(struct page_counter *counter, unsigned long nr_pages);
+
+static inline void page_counter_set_high(struct page_counter *counter,
+                                        unsigned long nr_pages)
+{
+       WRITE_ONCE(counter->high, nr_pages);
+}
+
 int page_counter_set_max(struct page_counter *counter, unsigned long nr_pages);
 int page_counter_memparse(const char *buf, const char *max,
                          unsigned long *nr_pages);
index a8f7bd8ea1c62983088a7811595a94a66b15c5be..8e085713150cb9c247bb9338561594d8f6a03996 100644 (file)
@@ -51,7 +51,10 @@ static inline void mapping_set_error(struct address_space *mapping, int error)
                return;
 
        /* Record in wb_err for checkers using errseq_t based tracking */
-       filemap_set_wb_err(mapping, error);
+       __filemap_set_wb_err(mapping, error);
+
+       /* Record it in superblock */
+       errseq_set(&mapping->host->i_sb->s_wb_err, error);
 
        /* Record it in flags for now, for legacy callers */
        if (error == -ENOSPC)
@@ -205,6 +208,43 @@ static inline int page_cache_add_speculative(struct page *page, int count)
        return __page_cache_add_speculative(page, count);
 }
 
+/**
+ * attach_page_private - Attach private data to a page.
+ * @page: Page to attach data to.
+ * @data: Data to attach to page.
+ *
+ * Attaching private data to a page increments the page's reference count.
+ * The data must be detached before the page will be freed.
+ */
+static inline void attach_page_private(struct page *page, void *data)
+{
+       get_page(page);
+       set_page_private(page, (unsigned long)data);
+       SetPagePrivate(page);
+}
+
+/**
+ * detach_page_private - Detach private data from a page.
+ * @page: Page to detach data from.
+ *
+ * Removes the data that was previously attached to the page and decrements
+ * the refcount on the page.
+ *
+ * Return: Data that was attached to the page.
+ */
+static inline void *detach_page_private(struct page *page)
+{
+       void *data = (void *)page_private(page);
+
+       if (!PagePrivate(page))
+               return NULL;
+       ClearPagePrivate(page);
+       set_page_private(page, 0);
+       put_page(page);
+
+       return data;
+}
+
 #ifdef CONFIG_NUMA
 extern struct page *__page_cache_alloc(gfp_t gfp);
 #else
@@ -615,6 +655,17 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask);
 void delete_from_page_cache_batch(struct address_space *mapping,
                                  struct pagevec *pvec);
 
+#define VM_READAHEAD_PAGES     (SZ_128K / PAGE_SIZE)
+
+void page_cache_sync_readahead(struct address_space *, struct file_ra_state *,
+               struct file *, pgoff_t index, unsigned long req_count);
+void page_cache_async_readahead(struct address_space *, struct file_ra_state *,
+               struct file *, struct page *, pgoff_t index,
+               unsigned long req_count);
+void page_cache_readahead_unbounded(struct address_space *, struct file *,
+               pgoff_t index, unsigned long nr_to_read,
+               unsigned long lookahead_count);
+
 /*
  * Like add_to_page_cache_locked, but used to add newly allocated pages:
  * the page is new, so we can just run __SetPageLocked() against it.
@@ -631,6 +682,146 @@ static inline int add_to_page_cache(struct page *page,
        return error;
 }
 
+/**
+ * struct readahead_control - Describes a readahead request.
+ *
+ * A readahead request is for consecutive pages.  Filesystems which
+ * implement the ->readahead method should call readahead_page() or
+ * readahead_page_batch() in a loop and attempt to start I/O against
+ * each page in the request.
+ *
+ * Most of the fields in this struct are private and should be accessed
+ * by the functions below.
+ *
+ * @file: The file, used primarily by network filesystems for authentication.
+ *       May be NULL if invoked internally by the filesystem.
+ * @mapping: Readahead this filesystem object.
+ */
+struct readahead_control {
+       struct file *file;
+       struct address_space *mapping;
+/* private: use the readahead_* accessors instead */
+       pgoff_t _index;
+       unsigned int _nr_pages;
+       unsigned int _batch_count;
+};
+
+/**
+ * readahead_page - Get the next page to read.
+ * @rac: The current readahead request.
+ *
+ * Context: The page is locked and has an elevated refcount.  The caller
+ * should decreases the refcount once the page has been submitted for I/O
+ * and unlock the page once all I/O to that page has completed.
+ * Return: A pointer to the next page, or %NULL if we are done.
+ */
+static inline struct page *readahead_page(struct readahead_control *rac)
+{
+       struct page *page;
+
+       BUG_ON(rac->_batch_count > rac->_nr_pages);
+       rac->_nr_pages -= rac->_batch_count;
+       rac->_index += rac->_batch_count;
+
+       if (!rac->_nr_pages) {
+               rac->_batch_count = 0;
+               return NULL;
+       }
+
+       page = xa_load(&rac->mapping->i_pages, rac->_index);
+       VM_BUG_ON_PAGE(!PageLocked(page), page);
+       rac->_batch_count = hpage_nr_pages(page);
+
+       return page;
+}
+
+static inline unsigned int __readahead_batch(struct readahead_control *rac,
+               struct page **array, unsigned int array_sz)
+{
+       unsigned int i = 0;
+       XA_STATE(xas, &rac->mapping->i_pages, 0);
+       struct page *page;
+
+       BUG_ON(rac->_batch_count > rac->_nr_pages);
+       rac->_nr_pages -= rac->_batch_count;
+       rac->_index += rac->_batch_count;
+       rac->_batch_count = 0;
+
+       xas_set(&xas, rac->_index);
+       rcu_read_lock();
+       xas_for_each(&xas, page, rac->_index + rac->_nr_pages - 1) {
+               VM_BUG_ON_PAGE(!PageLocked(page), page);
+               VM_BUG_ON_PAGE(PageTail(page), page);
+               array[i++] = page;
+               rac->_batch_count += hpage_nr_pages(page);
+
+               /*
+                * The page cache isn't using multi-index entries yet,
+                * so the xas cursor needs to be manually moved to the
+                * next index.  This can be removed once the page cache
+                * is converted.
+                */
+               if (PageHead(page))
+                       xas_set(&xas, rac->_index + rac->_batch_count);
+
+               if (i == array_sz)
+                       break;
+       }
+       rcu_read_unlock();
+
+       return i;
+}
+
+/**
+ * readahead_page_batch - Get a batch of pages to read.
+ * @rac: The current readahead request.
+ * @array: An array of pointers to struct page.
+ *
+ * Context: The pages are locked and have an elevated refcount.  The caller
+ * should decreases the refcount once the page has been submitted for I/O
+ * and unlock the page once all I/O to that page has completed.
+ * Return: The number of pages placed in the array.  0 indicates the request
+ * is complete.
+ */
+#define readahead_page_batch(rac, array)                               \
+       __readahead_batch(rac, array, ARRAY_SIZE(array))
+
+/**
+ * readahead_pos - The byte offset into the file of this readahead request.
+ * @rac: The readahead request.
+ */
+static inline loff_t readahead_pos(struct readahead_control *rac)
+{
+       return (loff_t)rac->_index * PAGE_SIZE;
+}
+
+/**
+ * readahead_length - The number of bytes in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline loff_t readahead_length(struct readahead_control *rac)
+{
+       return (loff_t)rac->_nr_pages * PAGE_SIZE;
+}
+
+/**
+ * readahead_index - The index of the first page in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline pgoff_t readahead_index(struct readahead_control *rac)
+{
+       return rac->_index;
+}
+
+/**
+ * readahead_count - The number of pages in this readahead request.
+ * @rac: The readahead request.
+ */
+static inline unsigned int readahead_count(struct readahead_control *rac)
+{
+       return rac->_nr_pages;
+}
+
 static inline unsigned long dir_pages(struct inode *inode)
 {
        return (unsigned long)(inode->i_size + PAGE_SIZE - 1) >>
index 12fc3482f5fc7ae1135d2a7f9a10358d88a9bcf9..89e2b23fb888e82edacdac2293d3ce5b70de78e5 100644 (file)
@@ -7,7 +7,8 @@
  * but could potentially be used anywhere else that simple option=arg
  * parsing is required.
  */
-
+#ifndef _LINUX_PARSER_H
+#define _LINUX_PARSER_H
 
 /* associates an integer enumerator with a pattern string. */
 struct match_token {
@@ -34,3 +35,5 @@ int match_hex(substring_t *, int *result);
 bool match_wildcard(const char *pattern, const char *str);
 size_t match_strlcpy(char *, const substring_t *, size_t);
 char *match_strdup(const substring_t *);
+
+#endif /* _LINUX_PARSER_H */
index 9c3e7619c929b7cbd74c497513bd8a728cf50a63..d7b610c4eebd851de82191d18eb8d7325af48872 100644 (file)
@@ -61,7 +61,7 @@ struct perf_guest_info_callbacks {
 
 struct perf_callchain_entry {
        __u64                           nr;
-       __u64                           ip[0]; /* /proc/sys/kernel/perf_event_max_stack */
+       __u64                           ip[]; /* /proc/sys/kernel/perf_event_max_stack */
 };
 
 struct perf_callchain_entry_ctx {
@@ -113,7 +113,7 @@ struct perf_raw_record {
 struct perf_branch_stack {
        __u64                           nr;
        __u64                           hw_idx;
-       struct perf_branch_entry        entries[0];
+       struct perf_branch_entry        entries[];
 };
 
 struct task_struct;
@@ -1305,7 +1305,7 @@ static inline int perf_is_paranoid(void)
 
 static inline int perf_allow_kernel(struct perf_event_attr *attr)
 {
-       if (sysctl_perf_event_paranoid > 1 && !capable(CAP_SYS_ADMIN))
+       if (sysctl_perf_event_paranoid > 1 && !perfmon_capable())
                return -EACCES;
 
        return security_perf_event_open(attr, PERF_SECURITY_KERNEL);
@@ -1313,7 +1313,7 @@ static inline int perf_allow_kernel(struct perf_event_attr *attr)
 
 static inline int perf_allow_cpu(struct perf_event_attr *attr)
 {
-       if (sysctl_perf_event_paranoid > 0 && !capable(CAP_SYS_ADMIN))
+       if (sysctl_perf_event_paranoid > 0 && !perfmon_capable())
                return -EACCES;
 
        return security_perf_event_open(attr, PERF_SECURITY_CPU);
@@ -1321,7 +1321,7 @@ static inline int perf_allow_cpu(struct perf_event_attr *attr)
 
 static inline int perf_allow_tracepoint(struct perf_event_attr *attr)
 {
-       if (sysctl_perf_event_paranoid > -1 && !capable(CAP_SYS_ADMIN))
+       if (sysctl_perf_event_paranoid > -1 && !perfmon_capable())
                return -EPERM;
 
        return security_perf_event_open(attr, PERF_SECURITY_TRACEPOINT);
index 02bef5177ff58336bfc2b39f24c58a4edb1284c2..69e261e2ca14cd3877c0214b74d8291aaf772870 100644 (file)
@@ -3,7 +3,7 @@
  * AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
  *
  * Copyright 2016 Qtechnology A/S
- * 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ * 2016 Ricardo Ribalda <ribalda@kernel.org>
  */
 #ifndef __LINUX_PLATFORM_DATA_AD5761_H__
 #define __LINUX_PLATFORM_DATA_AD5761_H__
diff --git a/include/linux/platform_data/gsc_hwmon.h b/include/linux/platform_data/gsc_hwmon.h
new file mode 100644 (file)
index 0000000..ec1611a
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _GSC_HWMON_H
+#define _GSC_HWMON_H
+
+enum gsc_hwmon_mode {
+       mode_temperature,
+       mode_voltage,
+       mode_voltage_raw,
+       mode_max,
+};
+
+/**
+ * struct gsc_hwmon_channel - configuration parameters
+ * @reg:  I2C register offset
+ * @mode: channel mode
+ * @name: channel name
+ * @mvoffset: voltage offset
+ * @vdiv: voltage divider array (2 resistor values in milli-ohms)
+ */
+struct gsc_hwmon_channel {
+       unsigned int reg;
+       unsigned int mode;
+       const char *name;
+       unsigned int mvoffset;
+       unsigned int vdiv[2];
+};
+
+/**
+ * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver
+ * @channels:  pointer to array of gsc_hwmon_channel structures
+ *             describing channels
+ * @nchannels: number of elements in @channels array
+ * @vreference: voltage reference (mV)
+ * @resolution: ADC bit resolution
+ * @fan_base: register base for FAN controller
+ */
+struct gsc_hwmon_platform_data {
+       const struct gsc_hwmon_channel *channels;
+       int nchannels;
+       unsigned int resolution;
+       unsigned int vreference;
+       unsigned int fan_base;
+};
+#endif
index 2ccdce6a4e27cbbe467fdab7468a533827f73223..45d860cac2b0c17e1f22cbf4948f686f346d1c60 100644 (file)
 #define ICH_RES_MEM_OFF                2
 #define ICH_RES_MEM_GCS_PMC    0
 
+/**
+ * struct itco_wdt_platform_data - iTCO_wdt platform data
+ * @name: Name of the platform
+ * @version: iTCO version
+ * @no_reboot_use_pmc: Use PMC BXT API to set and clear NO_REBOOT bit
+ */
 struct itco_wdt_platform_data {
        char name[32];
        unsigned int version;
-       /* private data to be passed to update_no_reboot_bit API */
-       void *no_reboot_priv;
-       /* pointer for platform specific no reboot update function */
-       int (*update_no_reboot_bit)(void *priv, bool set);
+       bool no_reboot_use_pmc;
 };
 
 #endif /* _ITCO_WDT_H_ */
index 0434f68eda8650976d1a9e2a338cad609e99041d..cba1184b364c97383565fa3ddd25f5fdeddf3a97 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright 2010 Wolfram Sang <w.sang@pengutronix.de>
+ * Copyright 2010 Wolfram Sang <kernel@pengutronix.de>
  */
 
 #ifndef __ASM_ARCH_IMX_ESDHC_H
diff --git a/include/linux/platform_data/mmc-esdhc-mcf.h b/include/linux/platform_data/mmc-esdhc-mcf.h
new file mode 100644 (file)
index 0000000..85cb786
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
+#define __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
+
+enum cd_types {
+       ESDHC_CD_NONE,          /* no CD, neither controller nor gpio */
+       ESDHC_CD_CONTROLLER,    /* mmc controller internal CD */
+       ESDHC_CD_PERMANENT,     /* no CD, card permanently wired to host */
+};
+
+struct mcf_esdhc_platform_data {
+       int max_bus_width;
+       int cd_type;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_MCF_ESDHC_H__ */
index d39fc658c3205c7456f116c7a692645bb897a342..897b8332a39f476f18053f77099720aa21e290ac 100644 (file)
@@ -85,6 +85,9 @@
 /* Maximum charging percentage */
 #define ASUS_WMI_DEVID_RSOC            0x00120057
 
+/* Keyboard dock */
+#define ASUS_WMI_DEVID_KBD_DOCK                0x00120063
+
 /* DSTS masks */
 #define ASUS_WMI_DSTS_STATUS_BIT       0x00000001
 #define ASUS_WMI_DSTS_UNKNOWN_BIT      0x00000002
index e057d1fa24694fedb22675b0520398c8805c85e4..121c104a4090e9b9c505a5199c6e0018e7e2269c 100644 (file)
@@ -544,31 +544,17 @@ struct pm_subsys_data {
  * These flags can be set by device drivers at the probe time.  They need not be
  * cleared by the drivers as the driver core will take care of that.
  *
- * NEVER_SKIP: Do not skip all system suspend/resume callbacks for the device.
- * SMART_PREPARE: Check the return value of the driver's ->prepare callback.
- * SMART_SUSPEND: No need to resume the device from runtime suspend.
- * LEAVE_SUSPENDED: Avoid resuming the device during system resume if possible.
- *
- * Setting SMART_PREPARE instructs bus types and PM domains which may want
- * system suspend/resume callbacks to be skipped for the device to return 0 from
- * their ->prepare callbacks if the driver's ->prepare callback returns 0 (in
- * other words, the system suspend/resume callbacks can only be skipped for the
- * device if its driver doesn't object against that).  This flag has no effect
- * if NEVER_SKIP is set.
- *
- * Setting SMART_SUSPEND instructs bus types and PM domains which may want to
- * runtime resume the device upfront during system suspend that doing so is not
- * necessary from the driver's perspective.  It also may cause them to skip
- * invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
- * the driver if they decide to leave the device in runtime suspend.
- *
- * Setting LEAVE_SUSPENDED informs the PM core and middle-layer code that the
- * driver prefers the device to be left in suspend after system resume.
+ * NO_DIRECT_COMPLETE: Do not apply direct-complete optimization to the device.
+ * SMART_PREPARE: Take the driver ->prepare callback return value into account.
+ * SMART_SUSPEND: Avoid resuming the device from runtime suspend.
+ * MAY_SKIP_RESUME: Allow driver "noirq" and "early" callbacks to be skipped.
+ *
+ * See Documentation/driver-api/pm/devices.rst for details.
  */
-#define DPM_FLAG_NEVER_SKIP            BIT(0)
+#define DPM_FLAG_NO_DIRECT_COMPLETE    BIT(0)
 #define DPM_FLAG_SMART_PREPARE         BIT(1)
 #define DPM_FLAG_SMART_SUSPEND         BIT(2)
-#define DPM_FLAG_LEAVE_SUSPENDED       BIT(3)
+#define DPM_FLAG_MAY_SKIP_RESUME       BIT(3)
 
 struct dev_pm_info {
        pm_message_t            power_state;
@@ -758,8 +744,8 @@ extern int pm_generic_poweroff_late(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 
-extern bool dev_pm_may_skip_resume(struct device *dev);
-extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
+extern bool dev_pm_skip_resume(struct device *dev);
+extern bool dev_pm_skip_suspend(struct device *dev);
 
 #else /* !CONFIG_PM_SLEEP */
 
index 3bdcbce8141ae437bd561cff194d2f7e5164f380..3dbc207bff53c0914b23b1c08aad6a8d62001cde 100644 (file)
@@ -102,9 +102,9 @@ static inline bool pm_runtime_enabled(struct device *dev)
        return !dev->power.disable_depth;
 }
 
-static inline bool pm_runtime_callbacks_present(struct device *dev)
+static inline bool pm_runtime_has_no_callbacks(struct device *dev)
 {
-       return !dev->power.no_callbacks;
+       return dev->power.no_callbacks;
 }
 
 static inline void pm_runtime_mark_last_busy(struct device *dev)
index bc3f1aecaa19486b6eb268e7f2c7cc6e4a4109c7..7d9c1c0e149c0d429d5ecbf9e015848fa1fdb8a4 100644 (file)
  *         PREEMPT_MASK:       0x000000ff
  *         SOFTIRQ_MASK:       0x0000ff00
  *         HARDIRQ_MASK:       0x000f0000
- *             NMI_MASK:       0x00100000
+ *             NMI_MASK:       0x00f00000
  * PREEMPT_NEED_RESCHED:       0x80000000
  */
 #define PREEMPT_BITS   8
 #define SOFTIRQ_BITS   8
 #define HARDIRQ_BITS   4
-#define NMI_BITS       1
+#define NMI_BITS       4
 
 #define PREEMPT_SHIFT  0
 #define SOFTIRQ_SHIFT  (PREEMPT_SHIFT + PREEMPT_BITS)
index e061635e04091bb555c8590a09c6f8f461c3edab..15c8133b194f91b4175e082d3061111ef0099c71 100644 (file)
@@ -279,39 +279,116 @@ static inline void printk_safe_flush_on_panic(void)
 
 extern int kptr_restrict;
 
+/**
+ * pr_fmt - used by the pr_*() macros to generate the printk format string
+ * @fmt: format string passed from a pr_*() macro
+ *
+ * This macro can be used to generate a unified format string for pr_*()
+ * macros. A common use is to prefix all pr_*() messages in a file with a common
+ * string. For example, defining this at the top of a source file:
+ *
+ *        #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ *
+ * would prefix all pr_info, pr_emerg... messages in the file with the module
+ * name.
+ */
 #ifndef pr_fmt
 #define pr_fmt(fmt) fmt
 #endif
 
-/*
- * These can be used to print at the various log levels.
- * All of these will print unconditionally, although note that pr_debug()
- * and other debug macros are compiled out unless either DEBUG is defined
- * or CONFIG_DYNAMIC_DEBUG is set.
+/**
+ * pr_emerg - Print an emergency-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_EMERG loglevel. It uses pr_fmt() to
+ * generate the format string.
  */
 #define pr_emerg(fmt, ...) \
        printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_alert - Print an alert-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_ALERT loglevel. It uses pr_fmt() to
+ * generate the format string.
+ */
 #define pr_alert(fmt, ...) \
        printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_crit - Print a critical-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_CRIT loglevel. It uses pr_fmt() to
+ * generate the format string.
+ */
 #define pr_crit(fmt, ...) \
        printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_err - Print an error-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_ERR loglevel. It uses pr_fmt() to
+ * generate the format string.
+ */
 #define pr_err(fmt, ...) \
        printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_warn - Print a warning-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_WARNING loglevel. It uses pr_fmt()
+ * to generate the format string.
+ */
 #define pr_warn(fmt, ...) \
        printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_notice - Print a notice-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_NOTICE loglevel. It uses pr_fmt() to
+ * generate the format string.
+ */
 #define pr_notice(fmt, ...) \
        printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
+/**
+ * pr_info - Print an info-level message
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_INFO loglevel. It uses pr_fmt() to
+ * generate the format string.
+ */
 #define pr_info(fmt, ...) \
        printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
-/*
- * Like KERN_CONT, pr_cont() should only be used when continuing
- * a line with no newline ('\n') enclosed. Otherwise it defaults
- * back to KERN_DEFAULT.
+
+/**
+ * pr_cont - Continues a previous log message in the same line.
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_CONT loglevel. It should only be
+ * used when continuing a log message with no newline ('\n') enclosed. Otherwise
+ * it defaults back to KERN_DEFAULT loglevel.
  */
 #define pr_cont(fmt, ...) \
        printk(KERN_CONT fmt, ##__VA_ARGS__)
 
-/* pr_devel() should produce zero code unless DEBUG is defined */
+/**
+ * pr_devel - Print a debug-level message conditionally
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to a printk with KERN_DEBUG loglevel if DEBUG is
+ * defined. Otherwise it does nothing.
+ *
+ * It uses pr_fmt() to generate the format string.
+ */
 #ifdef DEBUG
 #define pr_devel(fmt, ...) \
        printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
@@ -325,8 +402,19 @@ extern int kptr_restrict;
 #if defined(CONFIG_DYNAMIC_DEBUG)
 #include <linux/dynamic_debug.h>
 
-/* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
-#define pr_debug(fmt, ...) \
+/**
+ * pr_debug - Print a debug-level message conditionally
+ * @fmt: format string
+ * @...: arguments for the format string
+ *
+ * This macro expands to dynamic_pr_debug() if CONFIG_DYNAMIC_DEBUG is
+ * set. Otherwise, if DEBUG is defined, it's equivalent to a printk with
+ * KERN_DEBUG loglevel. If DEBUG is not defined it does nothing.
+ *
+ * It uses pr_fmt() to generate the format string (dynamic_pr_debug() uses
+ * pr_fmt() internally).
+ */
+#define pr_debug(fmt, ...)                     \
        dynamic_pr_debug(fmt, ##__VA_ARGS__)
 #elif defined(DEBUG)
 #define pr_debug(fmt, ...) \
@@ -384,8 +472,7 @@ extern int kptr_restrict;
        printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
 #define pr_info_once(fmt, ...)                                 \
        printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_cont_once(fmt, ...)                                 \
-       printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__)
+/* no pr_cont_once, don't do that... */
 
 #if defined(DEBUG)
 #define pr_devel_once(fmt, ...)                                        \
index d86de017c689c1278044a454d46e077e7aadccc6..c7b5f3db36aa810925ad15112a1a4e129404e318 100644 (file)
@@ -440,6 +440,9 @@ software_node_find_by_name(const struct software_node *parent,
 int software_node_register_nodes(const struct software_node *nodes);
 void software_node_unregister_nodes(const struct software_node *nodes);
 
+int software_node_register_node_group(const struct software_node **node_group);
+void software_node_unregister_node_group(const struct software_node **node_group);
+
 int software_node_register(const struct software_node *node);
 
 int software_node_notify(struct device *dev, unsigned long action);
index a67712b73b6cfb1fbad07ec728bda1f70df7ccfc..14ad9b9ebcd66eeb9744c657ec54c046bc2ddcbb 100644 (file)
@@ -21,11 +21,6 @@ bool psci_power_state_is_valid(u32 state);
 int psci_set_osi_mode(void);
 bool psci_has_osi_support(void);
 
-enum smccc_version {
-       SMCCC_VERSION_1_0,
-       SMCCC_VERSION_1_1,
-};
-
 struct psci_operations {
        u32 (*get_version)(void);
        int (*cpu_suspend)(u32 state, unsigned long entry_point);
@@ -35,8 +30,6 @@ struct psci_operations {
        int (*affinity_info)(unsigned long target_affinity,
                        unsigned long lowest_affinity_level);
        int (*migrate_info_type)(void);
-       enum arm_smccc_conduit conduit;
-       enum smccc_version smccc_version;
 };
 
 extern struct psci_operations psci_ops;
index 5167bf2bfc754a8cde1b46730c1c0f953a726dae..7fbc8679145c24213f872d9ec292de8047008635 100644 (file)
@@ -100,6 +100,8 @@ struct sev_data_init {
        u32 tmr_len;                    /* In */
 } __packed;
 
+#define SEV_INIT_FLAGS_SEV_ES  0x01
+
 /**
  * struct sev_data_pek_csr - PEK_CSR command parameters
  *
index e779441e6d26cbaa576abe7f4f89d11e91bbaa19..eb93a54cff31fa21d49b77012f14888b51e01bca 100644 (file)
@@ -96,6 +96,12 @@ struct pstore_record {
  *
  * @read_mutex:        serializes @open, @read, @close, and @erase callbacks
  * @flags:     bitfield of frontends the backend can accept writes for
+ * @max_reason:        Used when PSTORE_FLAGS_DMESG is set. Contains the
+ *             kmsg_dump_reason enum value. KMSG_DUMP_UNDEF means
+ *             "use existing kmsg_dump() filtering, based on the
+ *             printk.always_kmsg_dump boot param" (which is either
+ *             KMSG_DUMP_OOPS when false, or KMSG_DUMP_MAX when
+ *             true); see printk.always_kmsg_dump for more details.
  * @data:      backend-private pointer passed back during callbacks
  *
  * Callbacks:
@@ -170,7 +176,7 @@ struct pstore_record {
  */
 struct pstore_info {
        struct module   *owner;
-       char            *name;
+       const char      *name;
 
        struct semaphore buf_lock;
        char            *buf;
@@ -179,6 +185,7 @@ struct pstore_info {
        struct mutex    read_mutex;
 
        int             flags;
+       int             max_reason;
        void            *data;
 
        int             (*open)(struct pstore_info *psi);
diff --git a/include/linux/pstore_blk.h b/include/linux/pstore_blk.h
new file mode 100644 (file)
index 0000000..61e9145
--- /dev/null
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PSTORE_BLK_H_
+#define __PSTORE_BLK_H_
+
+#include <linux/types.h>
+#include <linux/pstore.h>
+#include <linux/pstore_zone.h>
+
+/**
+ * typedef pstore_blk_panic_write_op - panic write operation to block device
+ *
+ * @buf: the data to write
+ * @start_sect: start sector to block device
+ * @sects: sectors count on buf
+ *
+ * Return: On success, zero should be returned. Others excluding -ENOMSG
+ * mean error. -ENOMSG means to try next zone.
+ *
+ * Panic write to block device must be aligned to SECTOR_SIZE.
+ */
+typedef int (*pstore_blk_panic_write_op)(const char *buf, sector_t start_sect,
+               sector_t sects);
+
+/**
+ * struct pstore_blk_info - pstore/blk registration details
+ *
+ * @major:     Which major device number to support with pstore/blk
+ * @flags:     The supported PSTORE_FLAGS_* from linux/pstore.h.
+ * @panic_write:The write operation only used for the panic case.
+ *             This can be NULL, but is recommended to avoid losing
+ *             crash data if the kernel's IO path or work queues are
+ *             broken during a panic.
+ * @devt:      The dev_t that pstore/blk has attached to.
+ * @nr_sects:  Number of sectors on @devt.
+ * @start_sect:        Starting sector on @devt.
+ */
+struct pstore_blk_info {
+       unsigned int major;
+       unsigned int flags;
+       pstore_blk_panic_write_op panic_write;
+
+       /* Filled in by pstore/blk after registration. */
+       dev_t devt;
+       sector_t nr_sects;
+       sector_t start_sect;
+};
+
+int  register_pstore_blk(struct pstore_blk_info *info);
+void unregister_pstore_blk(unsigned int major);
+
+/**
+ * struct pstore_device_info - back-end pstore/blk driver structure.
+ *
+ * @total_size: The total size in bytes pstore/blk can use. It must be greater
+ *             than 4096 and be multiple of 4096.
+ * @flags:     Refer to macro starting with PSTORE_FLAGS defined in
+ *             linux/pstore.h. It means what front-ends this device support.
+ *             Zero means all backends for compatible.
+ * @read:      The general read operation. Both of the function parameters
+ *             @size and @offset are relative value to bock device (not the
+ *             whole disk).
+ *             On success, the number of bytes should be returned, others
+ *             means error.
+ * @write:     The same as @read, but the following error number:
+ *             -EBUSY means try to write again later.
+ *             -ENOMSG means to try next zone.
+ * @erase:     The general erase operation for device with special removing
+ *             job. Both of the function parameters @size and @offset are
+ *             relative value to storage.
+ *             Return 0 on success and others on failure.
+ * @panic_write:The write operation only used for panic case. It's optional
+ *             if you do not care panic log. The parameters are relative
+ *             value to storage.
+ *             On success, the number of bytes should be returned, others
+ *             excluding -ENOMSG mean error. -ENOMSG means to try next zone.
+ */
+struct pstore_device_info {
+       unsigned long total_size;
+       unsigned int flags;
+       pstore_zone_read_op read;
+       pstore_zone_write_op write;
+       pstore_zone_erase_op erase;
+       pstore_zone_write_op panic_write;
+};
+
+int  register_pstore_device(struct pstore_device_info *dev);
+void unregister_pstore_device(struct pstore_device_info *dev);
+
+/**
+ * struct pstore_blk_config - the pstore_blk backend configuration
+ *
+ * @device:            Name of the desired block device
+ * @max_reason:                Maximum kmsg dump reason to store to block device
+ * @kmsg_size:         Total size of for kmsg dumps
+ * @pmsg_size:         Total size of the pmsg storage area
+ * @console_size:      Total size of the console storage area
+ * @ftrace_size:       Total size for ftrace logging data (for all CPUs)
+ */
+struct pstore_blk_config {
+       char device[80];
+       enum kmsg_dump_reason max_reason;
+       unsigned long kmsg_size;
+       unsigned long pmsg_size;
+       unsigned long console_size;
+       unsigned long ftrace_size;
+};
+
+/**
+ * pstore_blk_get_config - get a copy of the pstore_blk backend configuration
+ *
+ * @info:      The sturct pstore_blk_config to be filled in
+ *
+ * Failure returns negative error code, and success returns 0.
+ */
+int pstore_blk_get_config(struct pstore_blk_config *info);
+
+#endif
index 9cb9b906729897255b7dc5477536c9dd28da3f3c..9f16afec7290d6e9c20e9c03192c19488162afca 100644 (file)
@@ -133,7 +133,7 @@ struct ramoops_platform_data {
        unsigned long   console_size;
        unsigned long   ftrace_size;
        unsigned long   pmsg_size;
-       int             dump_oops;
+       int             max_reason;
        u32             flags;
        struct persistent_ram_ecc_info ecc_info;
 };
diff --git a/include/linux/pstore_zone.h b/include/linux/pstore_zone.h
new file mode 100644 (file)
index 0000000..1e35eaa
--- /dev/null
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PSTORE_ZONE_H_
+#define __PSTORE_ZONE_H_
+
+#include <linux/types.h>
+
+typedef ssize_t (*pstore_zone_read_op)(char *, size_t, loff_t);
+typedef ssize_t (*pstore_zone_write_op)(const char *, size_t, loff_t);
+typedef ssize_t (*pstore_zone_erase_op)(size_t, loff_t);
+/**
+ * struct pstore_zone_info - pstore/zone back-end driver structure
+ *
+ * @owner:     Module which is responsible for this back-end driver.
+ * @name:      Name of the back-end driver.
+ * @total_size: The total size in bytes pstore/zone can use. It must be greater
+ *             than 4096 and be multiple of 4096.
+ * @kmsg_size: The size of oops/panic zone. Zero means disabled, otherwise,
+ *             it must be multiple of SECTOR_SIZE(512 Bytes).
+ * @max_reason: Maximum kmsg dump reason to store.
+ * @pmsg_size: The size of pmsg zone which is the same as @kmsg_size.
+ * @console_size:The size of console zone which is the same as @kmsg_size.
+ * @ftrace_size:The size of ftrace zone which is the same as @kmsg_size.
+ * @read:      The general read operation. Both of the function parameters
+ *             @size and @offset are relative value to storage.
+ *             On success, the number of bytes should be returned, others
+ *             mean error.
+ * @write:     The same as @read, but the following error number:
+ *             -EBUSY means try to write again later.
+ *             -ENOMSG means to try next zone.
+ * @erase:     The general erase operation for device with special removing
+ *             job. Both of the function parameters @size and @offset are
+ *             relative value to storage.
+ *             Return 0 on success and others on failure.
+ * @panic_write:The write operation only used for panic case. It's optional
+ *             if you do not care panic log. The parameters are relative
+ *             value to storage.
+ *             On success, the number of bytes should be returned, others
+ *             excluding -ENOMSG mean error. -ENOMSG means to try next zone.
+ */
+struct pstore_zone_info {
+       struct module *owner;
+       const char *name;
+
+       unsigned long total_size;
+       unsigned long kmsg_size;
+       int max_reason;
+       unsigned long pmsg_size;
+       unsigned long console_size;
+       unsigned long ftrace_size;
+       pstore_zone_read_op read;
+       pstore_zone_write_op write;
+       pstore_zone_erase_op erase;
+       pstore_zone_write_op panic_write;
+};
+
+extern int register_pstore_zone(struct pstore_zone_info *info);
+extern void unregister_pstore_zone(struct pstore_zone_info *info);
+
+#endif
index a67065c403c38e0db30c9d929757e8349a108671..2a3a955864259a53a95d988991e6fc2c502bb720 100644 (file)
@@ -13,7 +13,8 @@ struct ptdump_range {
 struct ptdump_state {
        /* level is 0:PGD to 4:PTE, or -1 if unknown */
        void (*note_page)(struct ptdump_state *st, unsigned long addr,
-                         int level, unsigned long val);
+                         int level, u64 val);
+       void (*effective_prot)(struct ptdump_state *st, int level, u64 val);
        const struct ptdump_range *range;
 };
 
index 63e62372443a501d33d42e6333419004ae614b6f..c2a9f7c90727385326ec141a5786ba40a05091ba 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/xarray.h>
+#include <linux/local_lock.h>
 
 /* Keep unconverted code working */
 #define radix_tree_root                xarray
 #define radix_tree_node                xa_node
 
+struct radix_tree_preload {
+       local_lock_t lock;
+       unsigned nr;
+       /* nodes->parent points to next preallocated node */
+       struct radix_tree_node *nodes;
+};
+DECLARE_PER_CPU(struct radix_tree_preload, radix_tree_preloads);
+
 /*
  * The bottom two bits of the slot determine how the remaining bits in the
  * slot are interpreted:
@@ -245,7 +254,7 @@ int radix_tree_tagged(const struct radix_tree_root *, unsigned int tag);
 
 static inline void radix_tree_preload_end(void)
 {
-       preempt_enable();
+       local_unlock(&radix_tree_preloads.lock);
 }
 
 void __rcu **idr_get_free(struct radix_tree_root *root,
index 1fd61a9af45cd6f8b4578c20eaf2e27bc6b8ba47..d7db179963221efc9c64ada85adf2de56128115e 100644 (file)
@@ -11,7 +11,7 @@
   I know it's not the cleaner way,  but in C (not in C++) to get
   performances and genericity...
 
-  See Documentation/rbtree.txt for documentation and samples.
+  See Documentation/core-api/rbtree.rst for documentation and samples.
 */
 
 #ifndef        _LINUX_RBTREE_H
index 724b0d036b5791afd2eb550a5cdb4c55b8c55a78..d1c53e9d8c7532173f5aa24725f553561f805b6d 100644 (file)
@@ -21,7 +21,7 @@
  * rb_insert_augmented() and rb_erase_augmented() are intended to be public.
  * The rest are implementation details you are not expected to depend on.
  *
- * See Documentation/rbtree.txt for documentation and samples.
+ * See Documentation/core-api/rbtree.rst for documentation and samples.
  */
 
 struct rb_augment_callbacks {
index 8214cdc715f261811df0e31d1d7cd00733802616..7375bb3da140b81b6aa9c045b54526cf23951d4e 100644 (file)
@@ -371,7 +371,7 @@ static inline void list_splice_tail_init_rcu(struct list_head *list,
  * @pos:       the type * to use as a loop cursor.
  * @head:      the head for your list.
  * @member:    the name of the list_head within the struct.
- * @cond...:   optional lockdep expression if called from non-RCU protection.
+ * @cond:      optional lockdep expression if called from non-RCU protection.
  *
  * This list-traversal primitive may safely run concurrently with
  * the _rcu list-mutation primitives such as list_add_rcu()
@@ -646,7 +646,7 @@ static inline void hlist_add_behind_rcu(struct hlist_node *n,
  * @pos:       the type * to use as a loop cursor.
  * @head:      the head for your list.
  * @member:    the name of the hlist_node within the struct.
- * @cond...:   optional lockdep expression if called from non-RCU protection.
+ * @cond:      optional lockdep expression if called from non-RCU protection.
  *
  * This list-traversal primitive may safely run concurrently with
  * the _rcu list-mutation primitives such as hlist_add_head_rcu()
index 2678a37c31696844a9f79615eb2e4906b61e68f9..659cbfa7581ac9607a7a22b4639884a00be0169c 100644 (file)
@@ -37,6 +37,7 @@
 /* Exported common interfaces */
 void call_rcu(struct rcu_head *head, rcu_callback_t func);
 void rcu_barrier_tasks(void);
+void rcu_barrier_tasks_rude(void);
 void synchronize_rcu(void);
 
 #ifdef CONFIG_PREEMPT_RCU
@@ -129,25 +130,57 @@ static inline void rcu_init_nohz(void) { }
  * Note a quasi-voluntary context switch for RCU-tasks's benefit.
  * This is a macro rather than an inline function to avoid #include hell.
  */
-#ifdef CONFIG_TASKS_RCU
-#define rcu_tasks_qs(t) \
-       do { \
-               if (READ_ONCE((t)->rcu_tasks_holdout)) \
-                       WRITE_ONCE((t)->rcu_tasks_holdout, false); \
+#ifdef CONFIG_TASKS_RCU_GENERIC
+
+# ifdef CONFIG_TASKS_RCU
+# define rcu_tasks_classic_qs(t, preempt)                              \
+       do {                                                            \
+               if (!(preempt) && READ_ONCE((t)->rcu_tasks_holdout))    \
+                       WRITE_ONCE((t)->rcu_tasks_holdout, false);      \
        } while (0)
-#define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t)
 void call_rcu_tasks(struct rcu_head *head, rcu_callback_t func);
 void synchronize_rcu_tasks(void);
+# else
+# define rcu_tasks_classic_qs(t, preempt) do { } while (0)
+# define call_rcu_tasks call_rcu
+# define synchronize_rcu_tasks synchronize_rcu
+# endif
+
+# ifdef CONFIG_TASKS_RCU_TRACE
+# define rcu_tasks_trace_qs(t)                                         \
+       do {                                                            \
+               if (!likely(READ_ONCE((t)->trc_reader_checked)) &&      \
+                   !unlikely(READ_ONCE((t)->trc_reader_nesting))) {    \
+                       smp_store_release(&(t)->trc_reader_checked, true); \
+                       smp_mb(); /* Readers partitioned by store. */   \
+               }                                                       \
+       } while (0)
+# else
+# define rcu_tasks_trace_qs(t) do { } while (0)
+# endif
+
+#define rcu_tasks_qs(t, preempt)                                       \
+do {                                                                   \
+       rcu_tasks_classic_qs((t), (preempt));                           \
+       rcu_tasks_trace_qs((t));                                        \
+} while (0)
+
+# ifdef CONFIG_TASKS_RUDE_RCU
+void call_rcu_tasks_rude(struct rcu_head *head, rcu_callback_t func);
+void synchronize_rcu_tasks_rude(void);
+# endif
+
+#define rcu_note_voluntary_context_switch(t) rcu_tasks_qs(t, false)
 void exit_tasks_rcu_start(void);
 void exit_tasks_rcu_finish(void);
-#else /* #ifdef CONFIG_TASKS_RCU */
-#define rcu_tasks_qs(t)        do { } while (0)
+#else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
+#define rcu_tasks_qs(t, preempt) do { } while (0)
 #define rcu_note_voluntary_context_switch(t) do { } while (0)
 #define call_rcu_tasks call_rcu
 #define synchronize_rcu_tasks synchronize_rcu
 static inline void exit_tasks_rcu_start(void) { }
 static inline void exit_tasks_rcu_finish(void) { }
-#endif /* #else #ifdef CONFIG_TASKS_RCU */
+#endif /* #else #ifdef CONFIG_TASKS_RCU_GENERIC */
 
 /**
  * cond_resched_tasks_rcu_qs - Report potential quiescent states to RCU
@@ -158,7 +191,7 @@ static inline void exit_tasks_rcu_finish(void) { }
  */
 #define cond_resched_tasks_rcu_qs() \
 do { \
-       rcu_tasks_qs(current); \
+       rcu_tasks_qs(current, false); \
        cond_resched(); \
 } while (0)
 
diff --git a/include/linux/rcupdate_trace.h b/include/linux/rcupdate_trace.h
new file mode 100644 (file)
index 0000000..4c25a41
--- /dev/null
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Read-Copy Update mechanism for mutual exclusion, adapted for tracing.
+ *
+ * Copyright (C) 2020 Paul E. McKenney.
+ */
+
+#ifndef __LINUX_RCUPDATE_TRACE_H
+#define __LINUX_RCUPDATE_TRACE_H
+
+#include <linux/sched.h>
+#include <linux/rcupdate.h>
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+
+extern struct lockdep_map rcu_trace_lock_map;
+
+static inline int rcu_read_lock_trace_held(void)
+{
+       return lock_is_held(&rcu_trace_lock_map);
+}
+
+#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+
+static inline int rcu_read_lock_trace_held(void)
+{
+       return 1;
+}
+
+#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+
+#ifdef CONFIG_TASKS_TRACE_RCU
+
+void rcu_read_unlock_trace_special(struct task_struct *t, int nesting);
+
+/**
+ * rcu_read_lock_trace - mark beginning of RCU-trace read-side critical section
+ *
+ * When synchronize_rcu_trace() is invoked by one task, then that task
+ * is guaranteed to block until all other tasks exit their read-side
+ * critical sections.  Similarly, if call_rcu_trace() is invoked on one
+ * task while other tasks are within RCU read-side critical sections,
+ * invocation of the corresponding RCU callback is deferred until after
+ * the all the other tasks exit their critical sections.
+ *
+ * For more details, please see the documentation for rcu_read_lock().
+ */
+static inline void rcu_read_lock_trace(void)
+{
+       struct task_struct *t = current;
+
+       WRITE_ONCE(t->trc_reader_nesting, READ_ONCE(t->trc_reader_nesting) + 1);
+       if (IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB) &&
+           t->trc_reader_special.b.need_mb)
+               smp_mb(); // Pairs with update-side barriers
+       rcu_lock_acquire(&rcu_trace_lock_map);
+}
+
+/**
+ * rcu_read_unlock_trace - mark end of RCU-trace read-side critical section
+ *
+ * Pairs with a preceding call to rcu_read_lock_trace(), and nesting is
+ * allowed.  Invoking a rcu_read_unlock_trace() when there is no matching
+ * rcu_read_lock_trace() is verboten, and will result in lockdep complaints.
+ *
+ * For more details, please see the documentation for rcu_read_unlock().
+ */
+static inline void rcu_read_unlock_trace(void)
+{
+       int nesting;
+       struct task_struct *t = current;
+
+       rcu_lock_release(&rcu_trace_lock_map);
+       nesting = READ_ONCE(t->trc_reader_nesting) - 1;
+       if (likely(!READ_ONCE(t->trc_reader_special.s)) || nesting) {
+               WRITE_ONCE(t->trc_reader_nesting, nesting);
+               return;  // We assume shallow reader nesting.
+       }
+       rcu_read_unlock_trace_special(t, nesting);
+}
+
+void call_rcu_tasks_trace(struct rcu_head *rhp, rcu_callback_t func);
+void synchronize_rcu_tasks_trace(void);
+void rcu_barrier_tasks_trace(void);
+
+#endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
+
+#endif /* __LINUX_RCUPDATE_TRACE_H */
index c0578ba23c1a9fa26ba05fca4ea6d326e9ee0f52..699b938358bfa418eb4e2ed43f4a1e3f7a7d513f 100644 (file)
@@ -31,4 +31,23 @@ do {                                                                 \
 
 #define wait_rcu_gp(...) _wait_rcu_gp(false, __VA_ARGS__)
 
+/**
+ * synchronize_rcu_mult - Wait concurrently for multiple grace periods
+ * @...: List of call_rcu() functions for different grace periods to wait on
+ *
+ * This macro waits concurrently for multiple types of RCU grace periods.
+ * For example, synchronize_rcu_mult(call_rcu, call_rcu_tasks) would wait
+ * on concurrent RCU and RCU-tasks grace periods.  Waiting on a given SRCU
+ * domain requires you to write a wrapper function for that SRCU domain's
+ * call_srcu() function, with this wrapper supplying the pointer to the
+ * corresponding srcu_struct.
+ *
+ * The first argument tells Tiny RCU's _wait_rcu_gp() not to
+ * bother waiting for RCU.  The reason for this is because anywhere
+ * synchronize_rcu_mult() can be called is automatically already a full
+ * grace period.
+ */
+#define synchronize_rcu_mult(...) \
+       _wait_rcu_gp(IS_ENABLED(CONFIG_TINY_RCU), __VA_ARGS__)
+
 #endif /* _LINUX_SCHED_RCUPDATE_WAIT_H */
index 045c28b71f4f3bd8a5250395d0fdd3fb232244cd..8512caeb768226be38de353f6524e36f93ccc7e3 100644 (file)
@@ -49,7 +49,7 @@ static inline void rcu_softirq_qs(void)
 #define rcu_note_context_switch(preempt) \
        do { \
                rcu_qs(); \
-               rcu_tasks_qs(current); \
+               rcu_tasks_qs(current, (preempt)); \
        } while (0)
 
 static inline int rcu_needs_cpu(u64 basemono, u64 *nextevt)
@@ -71,6 +71,8 @@ static inline void rcu_irq_enter(void) { }
 static inline void rcu_irq_exit_irqson(void) { }
 static inline void rcu_irq_enter_irqson(void) { }
 static inline void rcu_irq_exit(void) { }
+static inline void rcu_irq_exit_preempt(void) { }
+static inline void rcu_irq_exit_check_preempt(void) { }
 static inline void exit_rcu(void) { }
 static inline bool rcu_preempt_need_deferred_qs(struct task_struct *t)
 {
@@ -85,8 +87,10 @@ static inline void rcu_scheduler_starting(void) { }
 static inline void rcu_end_inkernel_boot(void) { }
 static inline bool rcu_inkernel_boot_has_ended(void) { return true; }
 static inline bool rcu_is_watching(void) { return true; }
+static inline bool __rcu_is_watching(void) { return true; }
 static inline void rcu_momentary_dyntick_idle(void) { }
 static inline void kfree_rcu_scheduler_running(void) { }
+static inline bool rcu_gp_might_be_stalled(void) { return false; }
 
 /* Avoid RCU read-side critical sections leaking across. */
 static inline void rcu_all_qs(void) { barrier(); }
index 45f3f66bb04df719f6e8350ab07bd33301ac070a..d5cc9d6759879465776a5478c72c326520d25656 100644 (file)
@@ -39,6 +39,7 @@ void rcu_barrier(void);
 bool rcu_eqs_special_set(int cpu);
 void rcu_momentary_dyntick_idle(void);
 void kfree_rcu_scheduler_running(void);
+bool rcu_gp_might_be_stalled(void);
 unsigned long get_state_synchronize_rcu(void);
 void cond_synchronize_rcu(unsigned long oldstate);
 
@@ -46,9 +47,16 @@ void rcu_idle_enter(void);
 void rcu_idle_exit(void);
 void rcu_irq_enter(void);
 void rcu_irq_exit(void);
+void rcu_irq_exit_preempt(void);
 void rcu_irq_enter_irqson(void);
 void rcu_irq_exit_irqson(void);
 
+#ifdef CONFIG_PROVE_RCU
+void rcu_irq_exit_check_preempt(void);
+#else
+static inline void rcu_irq_exit_check_preempt(void) { }
+#endif
+
 void exit_rcu(void);
 
 void rcu_scheduler_starting(void);
@@ -56,6 +64,7 @@ extern int rcu_scheduler_active __read_mostly;
 void rcu_end_inkernel_boot(void);
 bool rcu_inkernel_boot_has_ended(void);
 bool rcu_is_watching(void);
+bool __rcu_is_watching(void);
 #ifndef CONFIG_PREEMPTION
 void rcu_all_qs(void);
 #endif
index 40b07168fd8e371702df1e311a763703d0326612..cb666b9c6b6a5e5ec96f1666f8b8fbb4942ffc28 100644 (file)
 #include <linux/err.h>
 #include <linux/bug.h>
 #include <linux/lockdep.h>
+#include <linux/iopoll.h>
 
 struct module;
 struct clk;
 struct device;
+struct device_node;
 struct i2c_client;
 struct i3c_device;
 struct irq_domain;
@@ -71,6 +73,13 @@ struct reg_sequence {
        unsigned int delay_us;
 };
 
+#define REG_SEQ(_reg, _def, _delay_us) {               \
+                               .reg = _reg,            \
+                               .def = _def,            \
+                               .delay_us = _delay_us,  \
+                               }
+#define REG_SEQ0(_reg, _def)   REG_SEQ(_reg, _def, 0)
+
 #define        regmap_update_bits(map, reg, mask, val) \
        regmap_update_bits_base(map, reg, mask, val, NULL, false, false)
 #define        regmap_update_bits_async(map, reg, mask, val)\
@@ -122,26 +131,10 @@ struct reg_sequence {
  */
 #define regmap_read_poll_timeout(map, addr, val, cond, sleep_us, timeout_us) \
 ({ \
-       u64 __timeout_us = (timeout_us); \
-       unsigned long __sleep_us = (sleep_us); \
-       ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
-       int __ret; \
-       might_sleep_if(__sleep_us); \
-       for (;;) { \
-               __ret = regmap_read((map), (addr), &(val)); \
-               if (__ret) \
-                       break; \
-               if (cond) \
-                       break; \
-               if ((__timeout_us) && \
-                   ktime_compare(ktime_get(), __timeout) > 0) { \
-                       __ret = regmap_read((map), (addr), &(val)); \
-                       break; \
-               } \
-               if (__sleep_us) \
-                       usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
-       } \
-       __ret ?: ((cond) ? 0 : -ETIMEDOUT); \
+       int __ret, __tmp; \
+       __tmp = read_poll_timeout(regmap_read, __ret, __ret || (cond), \
+                       sleep_us, timeout_us, false, (map), (addr), &(val)); \
+       __ret ?: __tmp; \
 })
 
 /**
@@ -209,25 +202,10 @@ struct reg_sequence {
  */
 #define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_us) \
 ({ \
-       u64 __timeout_us = (timeout_us); \
-       unsigned long __sleep_us = (sleep_us); \
-       ktime_t timeout = ktime_add_us(ktime_get(), __timeout_us); \
-       int pollret; \
-       might_sleep_if(__sleep_us); \
-       for (;;) { \
-               pollret = regmap_field_read((field), &(val)); \
-               if (pollret) \
-                       break; \
-               if (cond) \
-                       break; \
-               if (__timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
-                       pollret = regmap_field_read((field), &(val)); \
-                       break; \
-               } \
-               if (__sleep_us) \
-                       usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
-       } \
-       pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
+       int __ret, __tmp; \
+       __tmp = read_poll_timeout(regmap_field_read, __ret, __ret || (cond), \
+                       sleep_us, timeout_us, false, (field), &(val)); \
+       __ret ?: __tmp; \
 })
 
 #ifdef CONFIG_REGMAP
@@ -1111,6 +1089,21 @@ bool regmap_reg_in_ranges(unsigned int reg,
                          const struct regmap_range *ranges,
                          unsigned int nranges);
 
+static inline int regmap_set_bits(struct regmap *map,
+                                 unsigned int reg, unsigned int bits)
+{
+       return regmap_update_bits_base(map, reg, bits, bits,
+                                      NULL, false, false);
+}
+
+static inline int regmap_clear_bits(struct regmap *map,
+                                   unsigned int reg, unsigned int bits)
+{
+       return regmap_update_bits_base(map, reg, bits, 0, NULL, false, false);
+}
+
+int regmap_test_bits(struct regmap *map, unsigned int reg, unsigned int bits);
+
 /**
  * struct reg_field - Description of an register field
  *
@@ -1134,6 +1127,14 @@ struct reg_field {
                                .msb = _msb,    \
                                }
 
+#define REG_FIELD_ID(_reg, _lsb, _msb, _size, _offset) {       \
+                               .reg = _reg,                    \
+                               .lsb = _lsb,                    \
+                               .msb = _msb,                    \
+                               .id_size = _size,               \
+                               .id_offset = _offset,           \
+                               }
+
 struct regmap_field *regmap_field_alloc(struct regmap *regmap,
                struct reg_field reg_field);
 void regmap_field_free(struct regmap_field *field);
@@ -1310,12 +1311,21 @@ struct regmap_irq_chip_data;
 int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                        int irq_base, const struct regmap_irq_chip *chip,
                        struct regmap_irq_chip_data **data);
+int regmap_add_irq_chip_np(struct device_node *np, struct regmap *map, int irq,
+                          int irq_flags, int irq_base,
+                          const struct regmap_irq_chip *chip,
+                          struct regmap_irq_chip_data **data);
 void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
 
 int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq,
                             int irq_flags, int irq_base,
                             const struct regmap_irq_chip *chip,
                             struct regmap_irq_chip_data **data);
+int devm_regmap_add_irq_chip_np(struct device *dev, struct device_node *np,
+                               struct regmap *map, int irq, int irq_flags,
+                               int irq_base,
+                               const struct regmap_irq_chip *chip,
+                               struct regmap_irq_chip_data **data);
 void devm_regmap_del_irq_chip(struct device *dev, int irq,
                              struct regmap_irq_chip_data *data);
 
@@ -1410,6 +1420,27 @@ static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg,
        return -EINVAL;
 }
 
+static inline int regmap_set_bits(struct regmap *map,
+                                 unsigned int reg, unsigned int bits)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
+static inline int regmap_clear_bits(struct regmap *map,
+                                   unsigned int reg, unsigned int bits)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
+static inline int regmap_test_bits(struct regmap *map,
+                                  unsigned int reg, unsigned int bits)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_field_update_bits_base(struct regmap_field *field,
                                        unsigned int mask, unsigned int val,
                                        bool *change, bool async, bool force)
index 0212d6255e4e8dfdd34ff3673fa93b5fc5c3215d..5f86824bd11750db4bc47a9eea4092a11390acbf 100644 (file)
@@ -62,6 +62,8 @@ int regulator_get_voltage_rdev(struct regulator_dev *rdev);
 int regulator_set_voltage_rdev(struct regulator_dev *rdev,
                               int min_uV, int max_uV,
                               suspend_state_t state);
+int regulator_do_balance_voltage(struct regulator_dev *rdev,
+                                suspend_state_t state, bool skip_coupled);
 #else
 static inline int regulator_coupler_register(struct regulator_coupler *coupler)
 {
@@ -92,6 +94,12 @@ static inline int regulator_set_voltage_rdev(struct regulator_dev *rdev,
 {
        return -EINVAL;
 }
+static inline int regulator_do_balance_voltage(struct regulator_dev *rdev,
+                                              suspend_state_t state,
+                                              bool skip_coupled)
+{
+       return -EINVAL;
+}
 #endif
 
 #endif
index 29d920516e0b842ef3d7049e3c57087ead46e648..7eb9fea8e482ac0aa096aad70058decca03db3ff 100644 (file)
@@ -13,6 +13,7 @@
 #define __LINUX_REGULATOR_DRIVER_H_
 
 #include <linux/device.h>
+#include <linux/linear_range.h>
 #include <linux/notifier.h>
 #include <linux/regulator/consumer.h>
 #include <linux/ww_mutex.h>
@@ -39,31 +40,13 @@ enum regulator_status {
        REGULATOR_STATUS_UNDEFINED,
 };
 
-/**
- * struct regulator_linear_range - specify linear voltage ranges
- *
- * Specify a range of voltages for regulator_map_linear_range() and
- * regulator_list_linear_range().
- *
- * @min_uV:  Lowest voltage in range
- * @min_sel: Lowest selector for range
- * @max_sel: Highest selector for range
- * @uV_step: Step size
- */
-struct regulator_linear_range {
-       unsigned int min_uV;
-       unsigned int min_sel;
-       unsigned int max_sel;
-       unsigned int uV_step;
-};
-
-/* Initialize struct regulator_linear_range */
+/* Initialize struct linear_range for regulators */
 #define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV)  \
 {                                                                      \
-       .min_uV         = _min_uV,                                      \
+       .min            = _min_uV,                                      \
        .min_sel        = _min_sel,                                     \
        .max_sel        = _max_sel,                                     \
-       .uV_step        = _step_uV,                                     \
+       .step           = _step_uV,                                     \
 }
 
 /**
@@ -348,7 +331,7 @@ struct regulator_desc {
        unsigned int ramp_delay;
        int min_dropout_uV;
 
-       const struct regulator_linear_range *linear_ranges;
+       const struct linear_range *linear_ranges;
        const unsigned int *linear_range_selectors;
 
        int n_linear_ranges;
index c759f96e39c18e7beeca4f8e2eca00f9ab68bca0..e13a333e7c3741e12f610eadbf780ae51a308a3f 100644 (file)
@@ -141,7 +141,7 @@ struct rchan_callbacks
         * cause relay_open() to create a single global buffer rather
         * than the default set of per-cpu buffers.
         *
-        * See Documentation/filesystems/relay.txt for more info.
+        * See Documentation/filesystems/relay.rst for more info.
         */
        struct dentry *(*create_buf_file)(const char *filename,
                                          struct dentry *parent,
index 4418f5cb832431519edb88c047522c12a816fa3b..33bb7c539246cc0dac3b93f60fbc81e9bde37b28 100644 (file)
@@ -613,7 +613,7 @@ union rcu_special {
                u8                      blocked;
                u8                      need_qs;
                u8                      exp_hint; /* Hint for performance. */
-               u8                      deferred_qs;
+               u8                      need_mb; /* Readers need smp_mb(). */
        } b; /* Bits. */
        u32 s; /* Set of bits. */
 };
@@ -724,6 +724,14 @@ struct task_struct {
        struct list_head                rcu_tasks_holdout_list;
 #endif /* #ifdef CONFIG_TASKS_RCU */
 
+#ifdef CONFIG_TASKS_TRACE_RCU
+       int                             trc_reader_nesting;
+       int                             trc_ipi_to_cpu;
+       union rcu_special               trc_reader_special;
+       bool                            trc_reader_checked;
+       struct list_head                trc_holdout_list;
+#endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
+
        struct sched_info               sched_info;
 
        struct list_head                tasks;
@@ -1289,6 +1297,12 @@ struct task_struct {
        unsigned long                   prev_lowest_stack;
 #endif
 
+#ifdef CONFIG_X86_MCE
+       u64                             mce_addr;
+       u64                             mce_status;
+       struct callback_head            mce_kill_me;
+#endif
+
        /*
         * New fields for task_struct should be added above here, so that
         * they are included in the randomized portion of task_struct.
@@ -1481,7 +1495,8 @@ extern struct pid *cad_pid;
 #define PF_KSWAPD              0x00020000      /* I am kswapd */
 #define PF_MEMALLOC_NOFS       0x00040000      /* All allocation requests will inherit GFP_NOFS */
 #define PF_MEMALLOC_NOIO       0x00080000      /* All allocation requests will inherit GFP_NOIO */
-#define PF_LESS_THROTTLE       0x00100000      /* Throttle me less: I clean memory */
+#define PF_LOCAL_THROTTLE      0x00100000      /* Throttle writes only against the bdi I write to,
+                                                * I am cleaning dirty pages from some other bdi. */
 #define PF_KTHREAD             0x00200000      /* I am a kernel thread */
 #define PF_RANDOMIZE           0x00400000      /* Randomize virtual address space */
 #define PF_SWAPWRITE           0x00800000      /* Allowed to write to swap */
diff --git a/include/linux/scs.h b/include/linux/scs.h
new file mode 100644 (file)
index 0000000..6dec390
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Shadow Call Stack support.
+ *
+ * Copyright (C) 2019 Google LLC
+ */
+
+#ifndef _LINUX_SCS_H
+#define _LINUX_SCS_H
+
+#include <linux/gfp.h>
+#include <linux/poison.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+
+#ifdef CONFIG_SHADOW_CALL_STACK
+
+/*
+ * In testing, 1 KiB shadow stack size (i.e. 128 stack frames on a 64-bit
+ * architecture) provided ~40% safety margin on stack usage while keeping
+ * memory allocation overhead reasonable.
+ */
+#define SCS_SIZE               SZ_1K
+#define GFP_SCS                        (GFP_KERNEL | __GFP_ZERO)
+
+/* An illegal pointer value to mark the end of the shadow stack. */
+#define SCS_END_MAGIC          (0x5f6UL + POISON_POINTER_DELTA)
+
+/* Allocate a static per-CPU shadow stack */
+#define DEFINE_SCS(name)                                               \
+       DEFINE_PER_CPU(unsigned long [SCS_SIZE/sizeof(long)], name)     \
+
+#define task_scs(tsk)          (task_thread_info(tsk)->scs_base)
+#define task_scs_sp(tsk)       (task_thread_info(tsk)->scs_sp)
+
+void scs_init(void);
+int scs_prepare(struct task_struct *tsk, int node);
+void scs_release(struct task_struct *tsk);
+
+static inline void scs_task_reset(struct task_struct *tsk)
+{
+       /*
+        * Reset the shadow stack to the base address in case the task
+        * is reused.
+        */
+       task_scs_sp(tsk) = task_scs(tsk);
+}
+
+static inline unsigned long *__scs_magic(void *s)
+{
+       return (unsigned long *)(s + SCS_SIZE) - 1;
+}
+
+static inline bool task_scs_end_corrupted(struct task_struct *tsk)
+{
+       unsigned long *magic = __scs_magic(task_scs(tsk));
+       unsigned long sz = task_scs_sp(tsk) - task_scs(tsk);
+
+       return sz >= SCS_SIZE - 1 || READ_ONCE_NOCHECK(*magic) != SCS_END_MAGIC;
+}
+
+#else /* CONFIG_SHADOW_CALL_STACK */
+
+static inline void scs_init(void) {}
+static inline void scs_task_reset(struct task_struct *tsk) {}
+static inline int scs_prepare(struct task_struct *tsk, int node) { return 0; }
+static inline void scs_release(struct task_struct *tsk) {}
+static inline bool task_scs_end_corrupted(struct task_struct *tsk) { return false; }
+
+#endif /* CONFIG_SHADOW_CALL_STACK */
+
+#endif /* _LINUX_SCS_H */
index 05bacd2ab135049721dfcb27607489ddcfa52ff6..6bb1a3f0258c27810c14e8daeac99a77beb33108 100644 (file)
@@ -24,6 +24,14 @@ static inline void clear_siginfo(kernel_siginfo_t *info)
 
 #define SI_EXPANSION_SIZE (sizeof(struct siginfo) - sizeof(struct kernel_siginfo))
 
+static inline void copy_siginfo_to_external(siginfo_t *to,
+                                           const kernel_siginfo_t *from)
+{
+       memcpy(to, from, sizeof(*from));
+       memset(((char *)to) + sizeof(struct kernel_siginfo), 0,
+               SI_EXPANSION_SIZE);
+}
+
 int copy_siginfo_to_user(siginfo_t __user *to, const kernel_siginfo_t *from);
 int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from);
 
index cbc9162689d0f56c3f0fa7b8776df10cf10c4482..04019872c7bcef823b29645509346f19466463fc 100644 (file)
@@ -227,8 +227,8 @@ static inline int get_boot_cpu_id(void)
  */
 extern void arch_disable_smp_support(void);
 
-extern void arch_enable_nonboot_cpus_begin(void);
-extern void arch_enable_nonboot_cpus_end(void);
+extern void arch_thaw_secondary_cpus_begin(void);
+extern void arch_thaw_secondary_cpus_end(void);
 
 void smp_setup_processor_id(void);
 
index 38286de779e3c9cdf564f96aa27037471e5eb189..aac57b5b7c21d1ada59e45779e728892bd746445 100644 (file)
@@ -394,6 +394,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *                   for example doing DMA mapping.  Called from threaded
  *                   context.
  * @transfer_one: transfer a single spi_transfer.
+ *
  *                  - return 0 if the transfer is finished,
  *                  - return 1 if the transfer is still in progress. When
  *                    the driver is finished with this transfer it must
index 528c4baad09146aa815bdfe529dfd41978e3fe44..56614af83d4af515411438877c0e02be2d8c9655 100644 (file)
@@ -47,6 +47,7 @@ struct kstat {
        struct timespec64 ctime;
        struct timespec64 btime;                        /* File creation time */
        u64             blocks;
+       u64             mnt_id;
 };
 
 #endif
index 4fcc6fd0cbd63eb1955a7a82dc2a78ae1da3cad5..b960098acfb0de04d925c56e1a69032aa68cd2d6 100644 (file)
@@ -466,6 +466,12 @@ static inline bool system_entering_hibernation(void) { return false; }
 static inline bool hibernation_available(void) { return false; }
 #endif /* CONFIG_HIBERNATION */
 
+#ifdef CONFIG_HIBERNATION_SNAPSHOT_DEV
+int is_hibernate_resume_dev(const struct inode *);
+#else
+static inline int is_hibernate_resume_dev(const struct inode *i) { return 0; }
+#endif
+
 /* Hibernation and suspend events */
 #define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */
 #define PM_POST_HIBERNATION    0x0002 /* Hibernation finished */
index e1bbf7a16b2767ab08d53f9b297d5c1c87cc992f..e92176fc882427d215b17ae6af0e77708a2cd9f9 100644 (file)
@@ -183,12 +183,17 @@ enum {
 #define SWAP_CLUSTER_MAX 32UL
 #define COMPACT_CLUSTER_MAX SWAP_CLUSTER_MAX
 
-#define SWAP_MAP_MAX   0x3e    /* Max duplication count, in first swap_map */
-#define SWAP_MAP_BAD   0x3f    /* Note pageblock is bad, in first swap_map */
+/* Bit flag in swap_map */
 #define SWAP_HAS_CACHE 0x40    /* Flag page is cached, in first swap_map */
-#define SWAP_CONT_MAX  0x7f    /* Max count, in each swap_map continuation */
-#define COUNT_CONTINUED        0x80    /* See swap_map continuation for full count */
-#define SWAP_MAP_SHMEM 0xbf    /* Owned by shmem/tmpfs, in first swap_map */
+#define COUNT_CONTINUED        0x80    /* Flag swap_map continuation for full count */
+
+/* Special value in first swap_map */
+#define SWAP_MAP_MAX   0x3e    /* Max count */
+#define SWAP_MAP_BAD   0x3f    /* Note page is bad */
+#define SWAP_MAP_SHMEM 0xbf    /* Owned by shmem/tmpfs */
+
+/* Special value in each swap_map continuation */
+#define SWAP_CONT_MAX  0x7f    /* Max count */
 
 /*
  * We use this to track usage of a cluster. A cluster is a block of swap disk
@@ -247,6 +252,7 @@ struct swap_info_struct {
        unsigned int inuse_pages;       /* number of those currently in use */
        unsigned int cluster_next;      /* likely index for next allocation */
        unsigned int cluster_nr;        /* countdown to next cluster search */
+       unsigned int __percpu *cluster_next_cpu; /*percpu index for next allocation */
        struct percpu_cluster __percpu *percpu_cluster; /* per cpu's swap location */
        struct rb_root swap_extent_root;/* root of the swap extent rbtree */
        struct block_device *bdev;      /* swap device or bdev of swap file */
@@ -337,6 +343,7 @@ extern void activate_page(struct page *);
 extern void mark_page_accessed(struct page *);
 extern void lru_add_drain(void);
 extern void lru_add_drain_cpu(int cpu);
+extern void lru_add_drain_cpu_zone(struct zone *zone);
 extern void lru_add_drain_all(void);
 extern void rotate_reclaimable_page(struct page *page);
 extern void deactivate_file_page(struct page *page);
@@ -408,7 +415,6 @@ extern unsigned long total_swapcache_pages(void);
 extern void show_swap_cache_info(void);
 extern int add_to_swap(struct page *page);
 extern int add_to_swap_cache(struct page *, swp_entry_t, gfp_t);
-extern int __add_to_swap_cache(struct page *page, swp_entry_t entry);
 extern void __delete_from_swap_cache(struct page *, swp_entry_t entry);
 extern void delete_from_swap_cache(struct page *);
 extern void free_page_and_swap_cache(struct page *);
index 1815065d52f37a699e7d49df244f273a4f266a10..7c354c2955f51e78fbfd638dd3109f30a9398e0f 100644 (file)
@@ -428,6 +428,8 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length);
 #endif
 asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len);
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode);
+asmlinkage long sys_faccessat2(int dfd, const char __user *filename, int mode,
+                              int flags);
 asmlinkage long sys_chdir(const char __user *filename);
 asmlinkage long sys_fchdir(unsigned int fd);
 asmlinkage long sys_chroot(const char __user *filename);
@@ -1333,11 +1335,11 @@ static inline int ksys_chmod(const char __user *filename, umode_t mode)
        return do_fchmodat(AT_FDCWD, filename, mode);
 }
 
-extern long do_faccessat(int dfd, const char __user *filename, int mode);
+long do_faccessat(int dfd, const char __user *filename, int mode, int flags);
 
 static inline long ksys_access(const char __user *filename, int mode)
 {
-       return do_faccessat(AT_FDCWD, filename, mode);
+       return do_faccessat(AT_FDCWD, filename, mode, 0);
 }
 
 extern int do_fchownat(int dfd, const char __user *filename, uid_t user,
index 80bb865b3a33dffd81476e2b54216a98647ee434..86067dbe774517540d6e3b32e4ce63e456f9bcd9 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2007 SUSE Linux Products GmbH
  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
  *
- * Please see Documentation/filesystems/sysfs.txt for more information.
+ * Please see Documentation/filesystems/sysfs.rst for more information.
  */
 
 #ifndef _SYSFS_H_
index 5424bc6feac886b7dfc0bf8aad166f172797a7b7..c7e4247663602f5ea978187ad24e646dac9d87f4 100644 (file)
@@ -121,13 +121,7 @@ struct tboot {
 #define TBOOT_UUID     {0xff, 0x8d, 0x3c, 0x66, 0xb3, 0xe8, 0x82, 0x4b, 0xbf,\
                         0xaa, 0x19, 0xea, 0x4d, 0x5, 0x7a, 0x8}
 
-extern struct tboot *tboot;
-
-static inline int tboot_enabled(void)
-{
-       return tboot != NULL;
-}
-
+bool tboot_enabled(void);
 extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
 extern struct acpi_table_header *tboot_get_dmar_table(
index 6241f59e2d6fe19ca60b329c9eed162ed3de2cd7..629b66e6c161672c44dd23ee123aaeb866bc0488 100644 (file)
@@ -89,7 +89,7 @@ void _torture_stop_kthread(char *m, struct task_struct **tp);
 #ifdef CONFIG_PREEMPTION
 #define torture_preempt_schedule() preempt_schedule()
 #else
-#define torture_preempt_schedule()
+#define torture_preempt_schedule()     do { } while (0)
 #endif
 
 #endif /* __LINUX_TORTURE_H */
index c253461b1c4e662fd5754d4c23d015acec667ad9..4f8c90c93c2997b11ec787dfe707568347e45d29 100644 (file)
@@ -97,7 +97,7 @@ struct tcg_pcr_event {
        u32 event_type;
        u8 digest[20];
        u32 event_size;
-       u8 event[0];
+       u8 event[];
 } __packed;
 
 struct tcg_event_field {
index 67f016010aad5f7d6f5ea78b1407f8d2506d35b3..9861c89f93be4d13a9adb41220ebcb24bd5280f1 100644 (file)
@@ -378,6 +378,14 @@ extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
 static inline unsigned long user_access_save(void) { return 0UL; }
 static inline void user_access_restore(unsigned long flags) { }
 #endif
+#ifndef user_write_access_begin
+#define user_write_access_begin user_access_begin
+#define user_write_access_end user_access_end
+#endif
+#ifndef user_read_access_begin
+#define user_read_access_begin user_access_begin
+#define user_read_access_end user_access_end
+#endif
 
 #ifdef CONFIG_HARDENED_USERCOPY
 void usercopy_warn(const char *name, const char *detail, bool to_user,
index 6f6ade63b04cd9af7ac329bf4c2f64499cfc7336..e8a924eeea3d01c86c40766445c5661c395bce6c 100644 (file)
@@ -31,6 +31,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
 {
        unsigned int gso_type = 0;
        unsigned int thlen = 0;
+       unsigned int p_off = 0;
        unsigned int ip_proto;
 
        if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
@@ -68,7 +69,8 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
                if (!skb_partial_csum_set(skb, start, off))
                        return -EINVAL;
 
-               if (skb_transport_offset(skb) + thlen > skb_headlen(skb))
+               p_off = skb_transport_offset(skb) + thlen;
+               if (p_off > skb_headlen(skb))
                        return -EINVAL;
        } else {
                /* gso packets without NEEDS_CSUM do not set transport_offset.
@@ -92,23 +94,32 @@ retry:
                                return -EINVAL;
                        }
 
-                       if (keys.control.thoff + thlen > skb_headlen(skb) ||
+                       p_off = keys.control.thoff + thlen;
+                       if (p_off > skb_headlen(skb) ||
                            keys.basic.ip_proto != ip_proto)
                                return -EINVAL;
 
                        skb_set_transport_header(skb, keys.control.thoff);
+               } else if (gso_type) {
+                       p_off = thlen;
+                       if (p_off > skb_headlen(skb))
+                               return -EINVAL;
                }
        }
 
        if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
                u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size);
+               struct skb_shared_info *shinfo = skb_shinfo(skb);
 
-               skb_shinfo(skb)->gso_size = gso_size;
-               skb_shinfo(skb)->gso_type = gso_type;
+               /* Too small packets are not really GSO ones. */
+               if (skb->len - p_off > gso_size) {
+                       shinfo->gso_size = gso_size;
+                       shinfo->gso_type = gso_type;
 
-               /* Header must be checked, and gso_segs computed. */
-               skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
-               skb_shinfo(skb)->gso_segs = 0;
+                       /* Header must be checked, and gso_segs computed. */
+                       shinfo->gso_type |= SKB_GSO_DODGY;
+                       shinfo->gso_segs = 0;
+               }
        }
 
        return 0;
index a95d3cc74d79ba468a06502f8cb8acf808ab55a1..48bb681e6c2aeda4ef78b7c263cded21217d52bb 100644 (file)
@@ -88,8 +88,7 @@ struct vmap_area {
  *     Highlevel APIs for driver use
  */
 extern void vm_unmap_ram(const void *mem, unsigned int count);
-extern void *vm_map_ram(struct page **pages, unsigned int count,
-                               int node, pgprot_t prot);
+extern void *vm_map_ram(struct page **pages, unsigned int count, int node);
 extern void vm_unmap_aliases(void);
 
 #ifdef CONFIG_MMU
@@ -107,26 +106,16 @@ extern void *vzalloc(unsigned long size);
 extern void *vmalloc_user(unsigned long size);
 extern void *vmalloc_node(unsigned long size, int node);
 extern void *vzalloc_node(unsigned long size, int node);
-extern void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags);
 extern void *vmalloc_exec(unsigned long size);
 extern void *vmalloc_32(unsigned long size);
 extern void *vmalloc_32_user(unsigned long size);
-extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot);
+extern void *__vmalloc(unsigned long size, gfp_t gfp_mask);
 extern void *__vmalloc_node_range(unsigned long size, unsigned long align,
                        unsigned long start, unsigned long end, gfp_t gfp_mask,
                        pgprot_t prot, unsigned long vm_flags, int node,
                        const void *caller);
-#ifndef CONFIG_MMU
-extern void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags);
-static inline void *__vmalloc_node_flags_caller(unsigned long size, int node,
-                                               gfp_t flags, void *caller)
-{
-       return __vmalloc_node_flags(size, node, flags);
-}
-#else
-extern void *__vmalloc_node_flags_caller(unsigned long size,
-                                        int node, gfp_t flags, void *caller);
-#endif
+void *__vmalloc_node(unsigned long size, unsigned long align, gfp_t gfp_mask,
+               int node, const void *caller);
 
 extern void vfree(const void *addr);
 extern void vfree_atomic(const void *addr);
@@ -141,8 +130,22 @@ extern int remap_vmalloc_range_partial(struct vm_area_struct *vma,
 
 extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
                                                        unsigned long pgoff);
-void vmalloc_sync_mappings(void);
-void vmalloc_sync_unmappings(void);
+
+/*
+ * Architectures can set this mask to a combination of PGTBL_P?D_MODIFIED values
+ * and let generic vmalloc and ioremap code know when arch_sync_kernel_mappings()
+ * needs to be called.
+ */
+#ifndef ARCH_PAGE_TABLE_SYNC_MASK
+#define ARCH_PAGE_TABLE_SYNC_MASK 0
+#endif
+
+/*
+ * There is no default implementation for arch_sync_kernel_mappings(). It is
+ * relied upon the compiler to optimize calls out if ARCH_PAGE_TABLE_SYNC_MASK
+ * is 0.
+ */
+void arch_sync_kernel_mappings(unsigned long start, unsigned long end);
 
 /*
  *     Lowlevel-APIs (not for driver use!)
@@ -161,8 +164,6 @@ static inline size_t get_vm_area_size(const struct vm_struct *area)
 extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
 extern struct vm_struct *get_vm_area_caller(unsigned long size,
                                        unsigned long flags, const void *caller);
-extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
-                                       unsigned long start, unsigned long end);
 extern struct vm_struct *__get_vm_area_caller(unsigned long size,
                                        unsigned long flags,
                                        unsigned long start, unsigned long end,
@@ -170,11 +171,11 @@ extern struct vm_struct *__get_vm_area_caller(unsigned long size,
 extern struct vm_struct *remove_vm_area(const void *addr);
 extern struct vm_struct *find_vm_area(const void *addr);
 
-extern int map_vm_area(struct vm_struct *area, pgprot_t prot,
-                       struct page **pages);
 #ifdef CONFIG_MMU
 extern int map_kernel_range_noflush(unsigned long start, unsigned long size,
                                    pgprot_t prot, struct page **pages);
+int map_kernel_range(unsigned long start, unsigned long size, pgprot_t prot,
+               struct page **pages);
 extern void unmap_kernel_range_noflush(unsigned long addr, unsigned long size);
 extern void unmap_kernel_range(unsigned long addr, unsigned long size);
 static inline void set_vm_flush_reset_perms(void *addr)
@@ -191,14 +192,12 @@ map_kernel_range_noflush(unsigned long start, unsigned long size,
 {
        return size >> PAGE_SHIFT;
 }
+#define map_kernel_range map_kernel_range_noflush
 static inline void
 unmap_kernel_range_noflush(unsigned long addr, unsigned long size)
 {
 }
-static inline void
-unmap_kernel_range(unsigned long addr, unsigned long size)
-{
-}
+#define unmap_kernel_range unmap_kernel_range_noflush
 static inline void set_vm_flush_reset_perms(void *addr)
 {
 }
index feeb6be5cad6b28bc0466a11adff6c4bb1d5c645..898c890fc153ff7c1b412b33ce4e1a7085f7d8c5 100644 (file)
@@ -1149,4 +1149,6 @@ int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, i
                (wait)->flags = 0;                                              \
        } while (0)
 
+bool try_invoke_on_locked_down_task(struct task_struct *p, bool (*func)(struct task_struct *t, void *arg), void *arg);
+
 #endif /* _LINUX_WAIT_H */
index 417d9f37077a802d142f4826e1863b987e067a0f..1464ce6ffa31b232d6f27e291e3754d0b61dda4f 100644 (file)
@@ -37,15 +37,15 @@ struct watchdog_governor;
  *
  * The watchdog_ops structure contains a list of low-level operations
  * that control a watchdog device. It also contains the module that owns
- * these operations. The start and stop function are mandatory, all other
+ * these operations. The start function is mandatory, all other
  * functions are optional.
  */
 struct watchdog_ops {
        struct module *owner;
        /* mandatory operations */
        int (*start)(struct watchdog_device *);
-       int (*stop)(struct watchdog_device *);
        /* optional operations */
+       int (*stop)(struct watchdog_device *);
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
index 2219cce81ca485ee5ede686bca82a32c327c28d4..0fdbf653b173f1450964c3b17238d7e027dcd601 100644 (file)
@@ -20,7 +20,7 @@
  * zsmalloc mapping modes
  *
  * NOTE: These only make a difference when a mapped object spans pages.
- * They also have no effect when PGTABLE_MAPPING is selected.
+ * They also have no effect when ZSMALLOC_PGTABLE_MAPPING is selected.
  */
 enum zs_mapmode {
        ZS_MM_RW, /* normal read-write mapping */
index 38956969fd1231a86a3d0870a60613a5511d32fa..b1c8397341249e594b7800e9c31c789b0ba1fccc 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * cec-notifier.h - notify CEC drivers of physical address changes
  *
- * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
+ * Copyright 2016 Russell King.
  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  */
 
index 97bf4885a962ffc41896b5e840ea16e27aa66c98..46754ba9d7b708f02a7fa813db6b98ad0482226d 100644 (file)
@@ -26,13 +26,9 @@ static inline
 __wsum csum_and_copy_from_user (const void __user *src, void *dst,
                                      int len, __wsum sum, int *err_ptr)
 {
-       if (access_ok(src, len))
-               return csum_partial_copy_from_user(src, dst, len, sum, err_ptr);
-
-       if (len)
+       if (copy_from_user(dst, src, len))
                *err_ptr = -EFAULT;
-
-       return sum;
+       return csum_partial(dst, len, sum);
 }
 #endif
 
@@ -42,10 +38,8 @@ static __inline__ __wsum csum_and_copy_to_user
 {
        sum = csum_partial(src, len, sum);
 
-       if (access_ok(dst, len)) {
-               if (copy_to_user(dst, src, len) == 0)
-                       return sum;
-       }
+       if (copy_to_user(dst, src, len) == 0)
+               return sum;
        if (len)
                *err_ptr = -EFAULT;
 
index dd7026a000660e9890a50c9846d1027476be4fb6..0335bbd76552a1ab0b509ef051536c097c8a2615 100644 (file)
@@ -25,6 +25,7 @@ struct espintcp_ctx {
        struct espintcp_msg partial;
        void (*saved_data_ready)(struct sock *sk);
        void (*saved_write_space)(struct sock *sk);
+       void (*saved_destruct)(struct sock *sk);
        struct work_struct work;
        bool tx_running;
 };
index b219a8fe0950b99a88d057816dcf1b7cb48080a8..2ec062aaa9782b71cfd75279c8347d90cff14e81 100644 (file)
@@ -447,6 +447,16 @@ static inline int fib_num_tclassid_users(struct net *net)
 #endif
 int fib_unmerge(struct net *net);
 
+static inline bool nhc_l3mdev_matches_dev(const struct fib_nh_common *nhc,
+const struct net_device *dev)
+{
+       if (nhc->nhc_dev == dev ||
+           l3mdev_master_ifindex_rcu(nhc->nhc_dev) == dev->ifindex)
+               return true;
+
+       return false;
+}
+
 /* Exported by fib_semantics.c */
 int ip_fib_check_default(__be32 gw, struct net_device *dev);
 int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force);
@@ -479,6 +489,8 @@ void fib_nh_common_release(struct fib_nh_common *nhc);
 void fib_alias_hw_flags_set(struct net *net, const struct fib_rt_info *fri);
 void fib_trie_init(void);
 struct fib_table *fib_trie_table(u32 id, struct fib_table *alias);
+bool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags,
+                        const struct flowi4 *flp);
 
 static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
 {
index c440ccc861fc70b13f565dbcb138768a562a3e53..8c9f1a7188591327710c112c0c8db8bbb9ca19fb 100644 (file)
@@ -70,6 +70,7 @@ struct nh_grp_entry {
 };
 
 struct nh_group {
+       struct nh_group         *spare; /* spare group for removals */
        u16                     num_nh;
        bool                    mpath;
        bool                    has_v4;
@@ -136,21 +137,20 @@ static inline unsigned int nexthop_num_path(const struct nexthop *nh)
 {
        unsigned int rc = 1;
 
-       if (nexthop_is_multipath(nh)) {
+       if (nh->is_group) {
                struct nh_group *nh_grp;
 
                nh_grp = rcu_dereference_rtnl(nh->nh_grp);
-               rc = nh_grp->num_nh;
+               if (nh_grp->mpath)
+                       rc = nh_grp->num_nh;
        }
 
        return rc;
 }
 
 static inline
-struct nexthop *nexthop_mpath_select(const struct nexthop *nh, int nhsel)
+struct nexthop *nexthop_mpath_select(const struct nh_group *nhg, int nhsel)
 {
-       const struct nh_group *nhg = rcu_dereference_rtnl(nh->nh_grp);
-
        /* for_nexthops macros in fib_semantics.c grabs a pointer to
         * the nexthop before checking nhsel
         */
@@ -185,12 +185,14 @@ static inline bool nexthop_is_blackhole(const struct nexthop *nh)
 {
        const struct nh_info *nhi;
 
-       if (nexthop_is_multipath(nh)) {
-               if (nexthop_num_path(nh) > 1)
-                       return false;
-               nh = nexthop_mpath_select(nh, 0);
-               if (!nh)
+       if (nh->is_group) {
+               struct nh_group *nh_grp;
+
+               nh_grp = rcu_dereference_rtnl(nh->nh_grp);
+               if (nh_grp->num_nh > 1)
                        return false;
+
+               nh = nh_grp->nh_entries[0].nh;
        }
 
        nhi = rcu_dereference_rtnl(nh->nh_info);
@@ -216,16 +218,79 @@ struct fib_nh_common *nexthop_fib_nhc(struct nexthop *nh, int nhsel)
        BUILD_BUG_ON(offsetof(struct fib_nh, nh_common) != 0);
        BUILD_BUG_ON(offsetof(struct fib6_nh, nh_common) != 0);
 
-       if (nexthop_is_multipath(nh)) {
-               nh = nexthop_mpath_select(nh, nhsel);
-               if (!nh)
-                       return NULL;
+       if (nh->is_group) {
+               struct nh_group *nh_grp;
+
+               nh_grp = rcu_dereference_rtnl(nh->nh_grp);
+               if (nh_grp->mpath) {
+                       nh = nexthop_mpath_select(nh_grp, nhsel);
+                       if (!nh)
+                               return NULL;
+               }
        }
 
        nhi = rcu_dereference_rtnl(nh->nh_info);
        return &nhi->fib_nhc;
 }
 
+/* called from fib_table_lookup with rcu_lock */
+static inline
+struct fib_nh_common *nexthop_get_nhc_lookup(const struct nexthop *nh,
+                                            int fib_flags,
+                                            const struct flowi4 *flp,
+                                            int *nhsel)
+{
+       struct nh_info *nhi;
+
+       if (nh->is_group) {
+               struct nh_group *nhg = rcu_dereference(nh->nh_grp);
+               int i;
+
+               for (i = 0; i < nhg->num_nh; i++) {
+                       struct nexthop *nhe = nhg->nh_entries[i].nh;
+
+                       nhi = rcu_dereference(nhe->nh_info);
+                       if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
+                               *nhsel = i;
+                               return &nhi->fib_nhc;
+                       }
+               }
+       } else {
+               nhi = rcu_dereference(nh->nh_info);
+               if (fib_lookup_good_nhc(&nhi->fib_nhc, fib_flags, flp)) {
+                       *nhsel = 0;
+                       return &nhi->fib_nhc;
+               }
+       }
+
+       return NULL;
+}
+
+static inline bool nexthop_uses_dev(const struct nexthop *nh,
+                                   const struct net_device *dev)
+{
+       struct nh_info *nhi;
+
+       if (nh->is_group) {
+               struct nh_group *nhg = rcu_dereference(nh->nh_grp);
+               int i;
+
+               for (i = 0; i < nhg->num_nh; i++) {
+                       struct nexthop *nhe = nhg->nh_entries[i].nh;
+
+                       nhi = rcu_dereference(nhe->nh_info);
+                       if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
+                               return true;
+               }
+       } else {
+               nhi = rcu_dereference(nh->nh_info);
+               if (nhc_l3mdev_matches_dev(&nhi->fib_nhc, dev))
+                       return true;
+       }
+
+       return false;
+}
+
 static inline unsigned int fib_info_num_path(const struct fib_info *fi)
 {
        if (unlikely(fi->nh))
@@ -263,8 +328,11 @@ static inline struct fib6_nh *nexthop_fib6_nh(struct nexthop *nh)
 {
        struct nh_info *nhi;
 
-       if (nexthop_is_multipath(nh)) {
-               nh = nexthop_mpath_select(nh, 0);
+       if (nh->is_group) {
+               struct nh_group *nh_grp;
+
+               nh_grp = rcu_dereference_rtnl(nh->nh_grp);
+               nh = nexthop_mpath_select(nh_grp, 0);
                if (!nh)
                        return NULL;
        }
index 6f8e60c6fbc746ea7ed2c2ddc97bffdbb7da4fc1..cf5649a2e795f2d9e19668c8ce20467596993b09 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/cache.h>
 #include <linux/percpu.h>
 #include <linux/skbuff.h>
-#include <linux/cryptohash.h>
 #include <linux/kref.h>
 #include <linux/ktime.h>
 
index bf9eb482393322928e2c979c6169d2c8c1b22054..18cd4f418464d2c7fd26d6381d474e21dac60833 100644 (file)
@@ -135,6 +135,8 @@ struct tls_sw_context_tx {
        struct tls_rec *open_rec;
        struct list_head tx_list;
        atomic_t encrypt_pending;
+       /* protect crypto_wait with encrypt_pending */
+       spinlock_t encrypt_compl_lock;
        int async_notify;
        u8 async_capable:1;
 
@@ -155,6 +157,8 @@ struct tls_sw_context_rx {
        u8 async_capable:1;
        u8 decrypted:1;
        atomic_t decrypt_pending;
+       /* protect crypto_wait with decrypt_pending*/
+       spinlock_t decrypt_compl_lock;
        bool async_notify;
 };
 
index 1b28ce1aba07bf3b8b71a7cf5b4979b47f573672..325fdaa3bb66313ceccff78719eb4e99715ac2ae 100644 (file)
@@ -88,7 +88,7 @@ struct ib_uobject *__uobj_get_destroy(const struct uverbs_api_object *obj,
 
 static inline void uobj_put_destroy(struct ib_uobject *uobj)
 {
-       rdma_lookup_put_uobject(uobj, UVERBS_LOOKUP_WRITE);
+       rdma_lookup_put_uobject(uobj, UVERBS_LOOKUP_DESTROY);
 }
 
 static inline void uobj_put_read(struct ib_uobject *uobj)
index 27f5caa6299a3e3c4424cfd834d4d7fcc1be3c7d..bf9806fd1306547e8ac6bc6bec6df71dfd8c5d14 100644 (file)
@@ -113,10 +113,10 @@ TRACE_EVENT(erofs_readpage,
 
 TRACE_EVENT(erofs_readpages,
 
-       TP_PROTO(struct inode *inode, struct page *page, unsigned int nrpage,
+       TP_PROTO(struct inode *inode, pgoff_t start, unsigned int nrpage,
                bool raw),
 
-       TP_ARGS(inode, page, nrpage, raw),
+       TP_ARGS(inode, start, nrpage, raw),
 
        TP_STRUCT__entry(
                __field(dev_t,          dev     )
@@ -129,7 +129,7 @@ TRACE_EVENT(erofs_readpages,
        TP_fast_assign(
                __entry->dev    = inode->i_sb->s_dev;
                __entry->nid    = EROFS_I(inode)->nid;
-               __entry->start  = page->index;
+               __entry->start  = start;
                __entry->nrpage = nrpage;
                __entry->raw    = raw;
        ),
index d97adfc327f030100666e2ce03a30e227c65e501..24c2557c37f020559afbd2fc05a39b51899e0200 100644 (file)
@@ -1376,9 +1376,9 @@ TRACE_EVENT(f2fs_writepages,
 
 TRACE_EVENT(f2fs_readpages,
 
-       TP_PROTO(struct inode *inode, struct page *page, unsigned int nrpage),
+       TP_PROTO(struct inode *inode, pgoff_t start, unsigned int nrpage),
 
-       TP_ARGS(inode, page, nrpage),
+       TP_ARGS(inode, start, nrpage),
 
        TP_STRUCT__entry(
                __field(dev_t,  dev)
@@ -1390,7 +1390,7 @@ TRACE_EVENT(f2fs_readpages,
        TP_fast_assign(
                __entry->dev    = inode->i_sb->s_dev;
                __entry->ino    = inode->i_ino;
-               __entry->start  = page->index;
+               __entry->start  = start;
                __entry->nrpage = nrpage;
        ),
 
index b70583c32c08c98c7aa266a4dee5491a45e251bf..72b3ba93b0a5ae856431b8708fc2da5c40728cf4 100644 (file)
@@ -70,6 +70,38 @@ DEFINE_EVENT(regulator_basic, regulator_disable_complete,
 
 );
 
+DEFINE_EVENT(regulator_basic, regulator_bypass_enable,
+
+       TP_PROTO(const char *name),
+
+       TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_bypass_enable_complete,
+
+       TP_PROTO(const char *name),
+
+       TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_bypass_disable,
+
+       TP_PROTO(const char *name),
+
+       TP_ARGS(name)
+
+);
+
+DEFINE_EVENT(regulator_basic, regulator_bypass_disable_complete,
+
+       TP_PROTO(const char *name),
+
+       TP_ARGS(name)
+
+);
+
 /*
  * Events that take a range of numerical values, mostly for voltages
  * and so on.
index 85a33bea76f1a776584ca36927e2481d88052daa..10f5d1fa73476a9cb4fb5e21b24d25f3c599500b 100644 (file)
@@ -541,7 +541,6 @@ TRACE_EVENT(global_dirty_state,
        TP_STRUCT__entry(
                __field(unsigned long,  nr_dirty)
                __field(unsigned long,  nr_writeback)
-               __field(unsigned long,  nr_unstable)
                __field(unsigned long,  background_thresh)
                __field(unsigned long,  dirty_thresh)
                __field(unsigned long,  dirty_limit)
@@ -552,7 +551,6 @@ TRACE_EVENT(global_dirty_state,
        TP_fast_assign(
                __entry->nr_dirty       = global_node_page_state(NR_FILE_DIRTY);
                __entry->nr_writeback   = global_node_page_state(NR_WRITEBACK);
-               __entry->nr_unstable    = global_node_page_state(NR_UNSTABLE_NFS);
                __entry->nr_dirtied     = global_node_page_state(NR_DIRTIED);
                __entry->nr_written     = global_node_page_state(NR_WRITTEN);
                __entry->background_thresh = background_thresh;
@@ -560,12 +558,11 @@ TRACE_EVENT(global_dirty_state,
                __entry->dirty_limit    = global_wb_domain.dirty_limit;
        ),
 
-       TP_printk("dirty=%lu writeback=%lu unstable=%lu "
+       TP_printk("dirty=%lu writeback=%lu "
                  "bg_thresh=%lu thresh=%lu limit=%lu "
                  "dirtied=%lu written=%lu",
                  __entry->nr_dirty,
                  __entry->nr_writeback,
-                 __entry->nr_unstable,
                  __entry->background_thresh,
                  __entry->dirty_thresh,
                  __entry->dirty_limit,
index 3a3201e4618ef8c7445895b26f6eebbaea1574f9..f4a01305d9a65c14fe46652970ec3195a8bce61c 100644 (file)
@@ -855,9 +855,11 @@ __SYSCALL(__NR_clone3, sys_clone3)
 __SYSCALL(__NR_openat2, sys_openat2)
 #define __NR_pidfd_getfd 438
 __SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
+#define __NR_faccessat2 439
+__SYSCALL(__NR_faccessat2, sys_faccessat2)
 
 #undef __NR_syscalls
-#define __NR_syscalls 439
+#define __NR_syscalls 440
 
 /*
  * 32 bit systems traditionally used different
index 272dc69fa0801efa74c199a7414b936f453d9043..e58c9636741b2d96586cfb7736babe760e70d739 100644 (file)
@@ -367,8 +367,14 @@ struct vfs_ns_cap_data {
 
 #define CAP_AUDIT_READ         37
 
+/*
+ * Allow system performance and observability privileged operations
+ * using perf_events, i915_perf and other kernel subsystems
+ */
+
+#define CAP_PERFMON            38
 
-#define CAP_LAST_CAP         CAP_AUDIT_READ
+#define CAP_LAST_CAP         CAP_PERFMON
 
 #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
 
index 34c02e4290fe5b7970b432052ddbd0beb8943f8a..c6dd0215482efc693b9d9ec9a4a8848e09b87223 100644 (file)
@@ -36,6 +36,7 @@ typedef __s64 Elf64_Sxword;
 #define PT_LOPROC  0x70000000
 #define PT_HIPROC  0x7fffffff
 #define PT_GNU_EH_FRAME                0x6474e550
+#define PT_GNU_PROPERTY                0x6474e553
 
 #define PT_GNU_STACK   (PT_LOOS + 0x474e551)
 
@@ -367,6 +368,7 @@ typedef struct elf64_shdr {
  * Notes used in ET_CORE. Architectures export some of the arch register sets
  * using the corresponding note types via the PTRACE_GETREGSET and
  * PTRACE_SETREGSET requests.
+ * The note name for all these is "LINUX".
  */
 #define NT_PRSTATUS    1
 #define NT_PRFPREG     2
@@ -429,6 +431,9 @@ typedef struct elf64_shdr {
 #define NT_MIPS_FP_MODE        0x801           /* MIPS floating-point mode */
 #define NT_MIPS_MSA    0x802           /* MIPS SIMD registers */
 
+/* Note types with note name "GNU" */
+#define NT_GNU_PROPERTY_TYPE_0 5
+
 /* Note header in a PT_NOTE section */
 typedef struct elf32_note {
   Elf32_Word   n_namesz;       /* Name size */
@@ -443,4 +448,10 @@ typedef struct elf64_note {
   Elf64_Word n_type;   /* Content type */
 } Elf64_Nhdr;
 
+/* .note.gnu.property types for EM_AARCH64: */
+#define GNU_PROPERTY_AARCH64_FEATURE_1_AND     0xc0000000
+
+/* Bits for GNU_PROPERTY_AARCH64_FEATURE_1_BTI */
+#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI     (1U << 0)
+
 #endif /* _UAPI_LINUX_ELF_H */
index 7fde76366ba46f9bd78ad888610d822c909f089f..1711e57f78482eac6a2c67e9f18db7b226fef130 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * include/uapi/linux/ethtool_netlink.h - netlink interface for ethtool
  *
- * See Documentation/networking/ethtool-netlink.txt in kernel source tree for
+ * See Documentation/networking/ethtool-netlink.rst in kernel source tree for
  * doucumentation of the interface.
  */
 
index ca88b7bce55385b41203284196923d09250f2ea3..2f86b2ad6d7e9d6bd7478c369dc301899a37a791 100644 (file)
 #define DN_ATTRIB      0x00000020      /* File changed attibutes */
 #define DN_MULTISHOT   0x80000000      /* Don't remove notifier */
 
+/*
+ * The constants AT_REMOVEDIR and AT_EACCESS have the same value.  AT_EACCESS is
+ * meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to
+ * unlinkat.  The two functions do completely different things and therefore,
+ * the flags can be allowed to overlap.  For example, passing AT_REMOVEDIR to
+ * faccessat would be undefined behavior and thus treating it equivalent to
+ * AT_EACCESS is valid undefined behavior.
+ */
 #define AT_FDCWD               -100    /* Special value used to indicate
                                            openat should use the current
                                            working directory. */
 #define AT_SYMLINK_NOFOLLOW    0x100   /* Do not follow symbolic links.  */
+#define AT_EACCESS             0x200   /* Test access permitted for
+                                           effective IDs, not real IDs.  */
 #define AT_REMOVEDIR           0x200   /* Remove directory instead of
                                            unlinking file.  */
 #define AT_SYMLINK_FOLLOW      0x400   /* Follow symbolic links.  */
index 1acd2b179aeff6a1bd560d9a50421d48faeaf451..7e5b5c10a49c9cc188ade2e5c598c0973edebe06 100644 (file)
@@ -308,7 +308,7 @@ struct fw_cdev_event_iso_interrupt_mc {
 /**
  * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed
  * @closure:   See &fw_cdev_event_common;
- *             set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl
+ *             set by``FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE)`` ioctl
  * @type:      %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
  *             %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
  * @handle:    Reference by which an allocated resource can be deallocated
index a10e3cdc283948980c5dfa0b3bc050eddc9cd2ec..7875709ccfebff2a5903f73e753a015015729c5d 100644 (file)
@@ -19,7 +19,8 @@
 #define FSCRYPT_POLICY_FLAGS_PAD_MASK          0x03
 #define FSCRYPT_POLICY_FLAG_DIRECT_KEY         0x04
 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64     0x08
-#define FSCRYPT_POLICY_FLAGS_VALID             0x0F
+#define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32     0x10
+#define FSCRYPT_POLICY_FLAGS_VALID             0x1F
 
 /* Encryption algorithms */
 #define FSCRYPT_MODE_AES_256_XTS               1
index 428c7dde6b4b3761f9df65ed9181b50e1c9f7165..fdd632c833b4eaee6b48311086d608ba0f692c7f 100644 (file)
@@ -116,7 +116,7 @@ struct kvm_irq_level {
         * ACPI gsi notion of irq.
         * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
         * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
-        * For ARM: See Documentation/virt/kvm/api.txt
+        * For ARM: See Documentation/virt/kvm/api.rst
         */
        union {
                __u32 irq;
@@ -1107,7 +1107,7 @@ struct kvm_xen_hvm_config {
  *
  * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
  * the irqfd to operate in resampling mode for level triggered interrupt
- * emulation.  See Documentation/virt/kvm/api.txt.
+ * emulation.  See Documentation/virt/kvm/api.rst.
  */
 #define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
 
index 00c08120f3ba60c2719458fd8903dd953744a1df..27a39847d55ce13c25c6c19bd4eef5be8af3fa2b 100644 (file)
@@ -3,6 +3,7 @@
 #define LINUX_MMC_IOCTL_H
 
 #include <linux/types.h>
+#include <linux/major.h>
 
 struct mmc_ioc_cmd {
        /*
index 0549a5c622bf33a9c3c96999d1f2f0a4f0747e87..91b4c63d5cbf4f5f319aa5e342172ae1aa7aa9dd 100644 (file)
@@ -83,6 +83,8 @@ struct sev_user_data_status {
        __u32 guest_count;                      /* Out */
 } __packed;
 
+#define SEV_STATUS_FLAGS_CONFIG_ES     0x0100
+
 /**
  * struct sev_user_data_pek_csr - PEK_CSR command parameters
  *
index ad80a5c885d598231ccf207ac48d2d908eaf1b85..6df9348bb277e4d7e6987ce658fad5cec2a8833c 100644 (file)
@@ -123,7 +123,10 @@ struct statx {
        __u32   stx_dev_major;  /* ID of device containing file [uncond] */
        __u32   stx_dev_minor;
        /* 0x90 */
-       __u64   __spare2[14];   /* Spare space for future expansion */
+       __u64   stx_mnt_id;
+       __u64   __spare2;
+       /* 0xa0 */
+       __u64   __spare3[12];   /* Spare space for future expansion */
        /* 0x100 */
 };
 
@@ -148,9 +151,19 @@ struct statx {
 #define STATX_BLOCKS           0x00000400U     /* Want/got stx_blocks */
 #define STATX_BASIC_STATS      0x000007ffU     /* The stuff in the normal stat struct */
 #define STATX_BTIME            0x00000800U     /* Want/got stx_btime */
-#define STATX_ALL              0x00000fffU     /* All currently supported flags */
+#define STATX_MNT_ID           0x00001000U     /* Got stx_mnt_id */
+
 #define STATX__RESERVED                0x80000000U     /* Reserved for future struct statx expansion */
 
+#ifndef __KERNEL__
+/*
+ * This is deprecated, and shall remain the same value in the future.  To avoid
+ * confusion please use the equivalent (STATX_BASIC_STATS | STATX_BTIME)
+ * instead.
+ */
+#define STATX_ALL              0x00000fffU
+#endif
+
 /*
  * Attributes to be found in stx_attributes and masked in stx_attributes_mask.
  *
@@ -168,6 +181,7 @@ struct statx {
 #define STATX_ATTR_NODUMP              0x00000040 /* [I] File is not to be dumped */
 #define STATX_ATTR_ENCRYPTED           0x00000800 /* [I] File requires key to decrypt in fs */
 #define STATX_ATTR_AUTOMOUNT           0x00001000 /* Dir: Automount trigger */
+#define STATX_ATTR_MOUNT_ROOT          0x00002000 /* Root of a mount */
 #define STATX_ATTR_VERITY              0x00100000 /* [I] Verity protected file */
 
 
index 5f3b9fec7b5f4491ad9f38beea7447a305ff4fb0..ff7cfdc6cb44dc98dde15e2e39ebebd80ec8f916 100644 (file)
@@ -304,7 +304,7 @@ enum xfrm_attr_type_t {
        XFRMA_PROTO,            /* __u8 */
        XFRMA_ADDRESS_FILTER,   /* struct xfrm_address_filter */
        XFRMA_PAD,
-       XFRMA_OFFLOAD_DEV,      /* struct xfrm_state_offload */
+       XFRMA_OFFLOAD_DEV,      /* struct xfrm_user_offload */
        XFRMA_SET_MARK,         /* __u32 */
        XFRMA_SET_MARK_MASK,    /* __u32 */
        XFRMA_IF_ID,            /* __u32 */
index 7b1ec806f8f9390147f571d67cb5b7d02d400762..38ab7accb7beff0637c1db6669fe17668a95dd20 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 
-/* Documentation/ioctl/ioctl-number.rst */
+/* Documentation/userspace-api/ioctl/ioctl-number.rst */
 #define RDMA_IOCTL_MAGIC       0x1b
 #define RDMA_VERBS_IOCTL \
        _IOWR(RDMA_IOCTL_MAGIC, 1, struct ib_uverbs_ioctl_hdr)
index dab8b1151b5698719cc9bbb072cead21c8773181..d72beda824aa79537b33da889d99063ed27f9ac0 100644 (file)
@@ -28,7 +28,7 @@ static int __init no_initrd(char *str)
 
 __setup("noinitrd", no_initrd);
 
-static int __init early_initrd(char *p)
+static int __init early_initrdmem(char *p)
 {
        phys_addr_t start;
        unsigned long size;
@@ -43,6 +43,17 @@ static int __init early_initrd(char *p)
        }
        return 0;
 }
+early_param("initrdmem", early_initrdmem);
+
+/*
+ * This is here as the initrd keyword has been in use since 11/2018
+ * on ARM, PowerPC, and MIPS.
+ * It should not be; it is reserved for bootloaders.
+ */
+static int __init early_initrd(char *p)
+{
+       return early_initrdmem(p);
+}
 early_param("initrd", early_initrd);
 
 static int init_linuxrc(struct subprocess_info *info, struct cred *new)
index bd403ed3e4184041a32906e7b03245787778f39f..15303d58d9dbff39b020bbd0a5f8064437573c8a 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/mm.h>
 #include <linux/audit.h>
 #include <linux/numa.h>
+#include <linux/scs.h>
 
 #include <asm/pgtable.h>
 #include <linux/uaccess.h>
@@ -50,6 +51,13 @@ static struct sighand_struct init_sighand = {
        .signalfd_wqh   = __WAIT_QUEUE_HEAD_INITIALIZER(init_sighand.signalfd_wqh),
 };
 
+#ifdef CONFIG_SHADOW_CALL_STACK
+unsigned long init_shadow_call_stack[SCS_SIZE / sizeof(long)]
+               __init_task_data = {
+       [(SCS_SIZE / sizeof(long)) - 1] = SCS_END_MAGIC
+};
+#endif
+
 /*
  * Set up the first task table, touch at your own risk!. Base=0,
  * limit=0x1fffff (=2MB)
@@ -141,6 +149,11 @@ struct task_struct init_task
        .rcu_tasks_holdout_list = LIST_HEAD_INIT(init_task.rcu_tasks_holdout_list),
        .rcu_tasks_idle_cpu = -1,
 #endif
+#ifdef CONFIG_TASKS_TRACE_RCU
+       .trc_reader_nesting = 0,
+       .trc_reader_special.s = 0,
+       .trc_holdout_list = LIST_HEAD_INIT(init_task.trc_holdout_list),
+#endif
 #ifdef CONFIG_CPUSETS
        .mems_allowed_seq = SEQCNT_ZERO(init_task.mems_allowed_seq),
 #endif
index 4cb4130ced32936b5060e6b05daa23e03fd6aa1e..c332eb9d4841add15ea0220033f6b915cb8db263 100644 (file)
@@ -103,6 +103,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
 obj-$(CONFIG_IRQ_WORK) += irq_work.o
 obj-$(CONFIG_CPU_PM) += cpu_pm.o
 obj-$(CONFIG_BPF) += bpf/
+obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
 
 obj-$(CONFIG_PERF_EVENTS) += events/
 
index 916f5132a9848dfc46982557a2fa126aad792c55..cf6fe9107f5c0b9fc622d00c783d2c014ab658ac 100644 (file)
@@ -82,7 +82,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
        struct bpf_prog *fp;
 
        size = round_up(size, PAGE_SIZE);
-       fp = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+       fp = __vmalloc(size, gfp_flags);
        if (fp == NULL)
                return NULL;
 
@@ -232,7 +232,7 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
        if (ret)
                return NULL;
 
-       fp = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+       fp = __vmalloc(size, gfp_flags);
        if (fp == NULL) {
                __bpf_prog_uncharge(fp_old->aux->user, delta);
        } else {
@@ -262,10 +262,10 @@ void __bpf_prog_free(struct bpf_prog *fp)
 
 int bpf_prog_calc_tag(struct bpf_prog *fp)
 {
-       const u32 bits_offset = SHA_MESSAGE_BYTES - sizeof(__be64);
+       const u32 bits_offset = SHA1_BLOCK_SIZE - sizeof(__be64);
        u32 raw_size = bpf_prog_tag_scratch_size(fp);
-       u32 digest[SHA_DIGEST_WORDS];
-       u32 ws[SHA_WORKSPACE_WORDS];
+       u32 digest[SHA1_DIGEST_WORDS];
+       u32 ws[SHA1_WORKSPACE_WORDS];
        u32 i, bsize, psize, blocks;
        struct bpf_insn *dst;
        bool was_ld_map;
@@ -277,7 +277,7 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
        if (!raw)
                return -ENOMEM;
 
-       sha_init(digest);
+       sha1_init(digest);
        memset(ws, 0, sizeof(ws));
 
        /* We need to take out the map fd for the digest calculation
@@ -308,8 +308,8 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
        memset(&raw[psize], 0, raw_size - psize);
        raw[psize++] = 0x80;
 
-       bsize  = round_up(psize, SHA_MESSAGE_BYTES);
-       blocks = bsize / SHA_MESSAGE_BYTES;
+       bsize  = round_up(psize, SHA1_BLOCK_SIZE);
+       blocks = bsize / SHA1_BLOCK_SIZE;
        todo   = raw;
        if (bsize - psize >= sizeof(__be64)) {
                bits = (__be64 *)(todo + bsize - sizeof(__be64));
@@ -320,12 +320,12 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
        *bits = cpu_to_be64((psize - 1) << 3);
 
        while (blocks--) {
-               sha_transform(digest, todo, ws);
-               todo += SHA_MESSAGE_BYTES;
+               sha1_transform(digest, todo, ws);
+               todo += SHA1_BLOCK_SIZE;
        }
 
        result = (__force __be32 *)digest;
-       for (i = 0; i < SHA_DIGEST_WORDS; i++)
+       for (i = 0; i < SHA1_DIGEST_WORDS; i++)
                result[i] = cpu_to_be32(digest[i]);
        memcpy(fp->tag, result, sizeof(fp->tag));
 
@@ -1089,7 +1089,7 @@ static struct bpf_prog *bpf_prog_clone_create(struct bpf_prog *fp_other,
        gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO | gfp_extra_flags;
        struct bpf_prog *fp;
 
-       fp = __vmalloc(fp_other->pages * PAGE_SIZE, gfp_flags, PAGE_KERNEL);
+       fp = __vmalloc(fp_other->pages * PAGE_SIZE, gfp_flags);
        if (fp != NULL) {
                /* aux->prog still points to the fp_other one, so
                 * when promoting the clone to the real program,
index 4e6dee19a668f09ebab03d535a79b663c2b42ba5..42c7a42fc9c8ea8b5e8ac3b12b7fe982d5560a63 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/nospec.h>
 #include <linux/audit.h>
 #include <uapi/linux/btf.h>
+#include <asm/pgtable.h>
 #include <linux/bpf_lsm.h>
 
 #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -281,27 +282,29 @@ static void *__bpf_map_area_alloc(u64 size, int numa_node, bool mmapable)
         * __GFP_RETRY_MAYFAIL to avoid such situations.
         */
 
-       const gfp_t flags = __GFP_NOWARN | __GFP_ZERO;
+       const gfp_t gfp = __GFP_NOWARN | __GFP_ZERO;
+       unsigned int flags = 0;
+       unsigned long align = 1;
        void *area;
 
        if (size >= SIZE_MAX)
                return NULL;
 
        /* kmalloc()'ed memory can't be mmap()'ed */
-       if (!mmapable && size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
-               area = kmalloc_node(size, GFP_USER | __GFP_NORETRY | flags,
+       if (mmapable) {
+               BUG_ON(!PAGE_ALIGNED(size));
+               align = SHMLBA;
+               flags = VM_USERMAP;
+       } else if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
+               area = kmalloc_node(size, gfp | GFP_USER | __GFP_NORETRY,
                                    numa_node);
                if (area != NULL)
                        return area;
        }
-       if (mmapable) {
-               BUG_ON(!PAGE_ALIGNED(size));
-               return vmalloc_user_node_flags(size, numa_node, GFP_KERNEL |
-                                              __GFP_RETRY_MAYFAIL | flags);
-       }
-       return __vmalloc_node_flags_caller(size, numa_node,
-                                          GFP_KERNEL | __GFP_RETRY_MAYFAIL |
-                                          flags, __builtin_return_address(0));
+
+       return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
+                       gfp | GFP_KERNEL | __GFP_RETRY_MAYFAIL, PAGE_KERNEL,
+                       flags, numa_node, __builtin_return_address(0));
 }
 
 void *bpf_map_area_alloc(u64 size, int numa_node)
index 8d7ee40e2748441bd0036f9fc0528e37255c8d27..efe14cf24bc6564e6ef878690cd0a1ffacae728f 100644 (file)
@@ -1168,14 +1168,14 @@ static void __reg_assign_32_into_64(struct bpf_reg_state *reg)
         * but must be positive otherwise set to worse case bounds
         * and refine later from tnum.
         */
-       if (reg->s32_min_value > 0)
-               reg->smin_value = reg->s32_min_value;
-       else
-               reg->smin_value = 0;
-       if (reg->s32_max_value > 0)
+       if (reg->s32_min_value >= 0 && reg->s32_max_value >= 0)
                reg->smax_value = reg->s32_max_value;
        else
                reg->smax_value = U32_MAX;
+       if (reg->s32_min_value >= 0)
+               reg->smin_value = reg->s32_min_value;
+       else
+               reg->smin_value = 0;
 }
 
 static void __reg_combine_32_into_64(struct bpf_reg_state *reg)
@@ -10428,22 +10428,13 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env)
 }
 #define SECURITY_PREFIX "security_"
 
-static int check_attach_modify_return(struct bpf_verifier_env *env)
+static int check_attach_modify_return(struct bpf_prog *prog, unsigned long addr)
 {
-       struct bpf_prog *prog = env->prog;
-       unsigned long addr = (unsigned long) prog->aux->trampoline->func.addr;
-
-       /* This is expected to be cleaned up in the future with the KRSI effort
-        * introducing the LSM_HOOK macro for cleaning up lsm_hooks.h.
-        */
        if (within_error_injection_list(addr) ||
            !strncmp(SECURITY_PREFIX, prog->aux->attach_func_name,
                     sizeof(SECURITY_PREFIX) - 1))
                return 0;
 
-       verbose(env, "fmod_ret attach_btf_id %u (%s) is not modifiable\n",
-               prog->aux->attach_btf_id, prog->aux->attach_func_name);
-
        return -EINVAL;
 }
 
@@ -10654,11 +10645,18 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
                                goto out;
                        }
                }
+
+               if (prog->expected_attach_type == BPF_MODIFY_RETURN) {
+                       ret = check_attach_modify_return(prog, addr);
+                       if (ret)
+                               verbose(env, "%s() is not modifiable\n",
+                                       prog->aux->attach_func_name);
+               }
+
+               if (ret)
+                       goto out;
                tr->func.addr = (void *)addr;
                prog->aux->trampoline = tr;
-
-               if (prog->expected_attach_type == BPF_MODIFY_RETURN)
-                       ret = check_attach_modify_return(env);
 out:
                mutex_unlock(&tr->mutex);
                if (ret)
index 6f87352f8219cddbd96e3c439e88c3910c15fa7f..41ca996568dfbbfdc9d1e2dc7841ccd6ff819a56 100644 (file)
@@ -33,12 +33,9 @@ void cgroup_rstat_updated(struct cgroup *cgrp, int cpu)
                return;
 
        /*
-        * Paired with the one in cgroup_rstat_cpu_pop_updated().  Either we
-        * see NULL updated_next or they see our updated stat.
-        */
-       smp_mb();
-
-       /*
+        * Speculative already-on-list test. This may race leading to
+        * temporary inaccuracies, which is fine.
+        *
         * Because @parent's updated_children is terminated with @parent
         * instead of NULL, we can tell whether @cgrp is on the list by
         * testing the next pointer for NULL.
@@ -134,13 +131,6 @@ static struct cgroup *cgroup_rstat_cpu_pop_updated(struct cgroup *pos,
                *nextp = rstatc->updated_next;
                rstatc->updated_next = NULL;
 
-               /*
-                * Paired with the one in cgroup_rstat_cpu_updated().
-                * Either they see NULL updated_next or we see their
-                * updated stat.
-                */
-               smp_mb();
-
                return pos;
        }
 
index 843dd17e6078b6d530ecc46260703b390f9abbe4..b8d2800bb4b719eb8ad5407823415fe1e0daa8d9 100644 (file)
@@ -199,7 +199,7 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
        bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
        nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
 
-       if (!user_access_begin(umask, bitmap_size / 8))
+       if (!user_read_access_begin(umask, bitmap_size / 8))
                return -EFAULT;
 
        while (nr_compat_longs > 1) {
@@ -211,11 +211,11 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
        }
        if (nr_compat_longs)
                unsafe_get_user(*mask, umask++, Efault);
-       user_access_end();
+       user_read_access_end();
        return 0;
 
 Efault:
-       user_access_end();
+       user_read_access_end();
        return -EFAULT;
 }
 
@@ -228,7 +228,7 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
        bitmap_size = ALIGN(bitmap_size, BITS_PER_COMPAT_LONG);
        nr_compat_longs = BITS_TO_COMPAT_LONGS(bitmap_size);
 
-       if (!user_access_begin(umask, bitmap_size / 8))
+       if (!user_write_access_begin(umask, bitmap_size / 8))
                return -EFAULT;
 
        while (nr_compat_longs > 1) {
@@ -239,10 +239,10 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
        }
        if (nr_compat_longs)
                unsafe_put_user((compat_ulong_t)*mask, umask++, Efault);
-       user_access_end();
+       user_write_access_end();
        return 0;
 Efault:
-       user_access_end();
+       user_write_access_end();
        return -EFAULT;
 }
 
index 2371292f30b03daa463706a2d9150c9c3bae5861..9f892144db6b99359ab89b15cf66faa371688a38 100644 (file)
@@ -432,7 +432,7 @@ static inline bool cpu_smt_allowed(unsigned int cpu)
        /*
         * On x86 it's required to boot all logical CPUs at least once so
         * that the init code can get a chance to set CR4.MCE on each
-        * CPU. Otherwise, a broadacasted MCE observing CR4.MCE=0b on any
+        * CPU. Otherwise, a broadcasted MCE observing CR4.MCE=0b on any
         * core will shutdown the machine.
         */
        return !cpumask_test_cpu(cpu, &cpus_booted_once_mask);
@@ -1327,7 +1327,7 @@ void bringup_nonboot_cpus(unsigned int setup_max_cpus)
 #ifdef CONFIG_PM_SLEEP_SMP
 static cpumask_var_t frozen_cpus;
 
-int __freeze_secondary_cpus(int primary, bool suspend)
+int freeze_secondary_cpus(int primary)
 {
        int cpu, error = 0;
 
@@ -1352,7 +1352,7 @@ int __freeze_secondary_cpus(int primary, bool suspend)
                if (cpu == primary)
                        continue;
 
-               if (suspend && pm_wakeup_pending()) {
+               if (pm_wakeup_pending()) {
                        pr_info("Wakeup pending. Abort CPU freeze\n");
                        error = -EBUSY;
                        break;
@@ -1376,8 +1376,8 @@ int __freeze_secondary_cpus(int primary, bool suspend)
 
        /*
         * Make sure the CPUs won't be enabled by someone else. We need to do
-        * this even in case of failure as all disable_nonboot_cpus() users are
-        * supposed to do enable_nonboot_cpus() on the failure path.
+        * this even in case of failure as all freeze_secondary_cpus() users are
+        * supposed to do thaw_secondary_cpus() on the failure path.
         */
        cpu_hotplug_disabled++;
 
@@ -1385,15 +1385,15 @@ int __freeze_secondary_cpus(int primary, bool suspend)
        return error;
 }
 
-void __weak arch_enable_nonboot_cpus_begin(void)
+void __weak arch_thaw_secondary_cpus_begin(void)
 {
 }
 
-void __weak arch_enable_nonboot_cpus_end(void)
+void __weak arch_thaw_secondary_cpus_end(void)
 {
 }
 
-void enable_nonboot_cpus(void)
+void thaw_secondary_cpus(void)
 {
        int cpu, error;
 
@@ -1405,7 +1405,7 @@ void enable_nonboot_cpus(void)
 
        pr_info("Enabling non-boot CPUs ...\n");
 
-       arch_enable_nonboot_cpus_begin();
+       arch_thaw_secondary_cpus_begin();
 
        for_each_cpu(cpu, frozen_cpus) {
                trace_suspend_resume(TPS("CPU_ON"), cpu, true);
@@ -1418,7 +1418,7 @@ void enable_nonboot_cpus(void)
                pr_warn("Error taking CPU%d up: %d\n", cpu, error);
        }
 
-       arch_enable_nonboot_cpus_end();
+       arch_thaw_secondary_cpus_end();
 
        cpumask_clear(frozen_cpus);
 out:
index 9c23ae074b4011cf53ca0fb96c4638cee5e21a28..92da32275af51eae116bdfa56b93813cdceb644e 100644 (file)
@@ -5,12 +5,6 @@
 #include <linux/errno.h>
 #include <linux/export.h>
 
-/*
- * If we have booted due to a crash, max_pfn will be a very low value. We need
- * to know the amount of memory that the previous kernel used.
- */
-unsigned long saved_max_pfn;
-
 /*
  * stores the physical address of elf header of crash image
  *
index d14cbc83986a3fa76e463d40c1972f9aa82bb2cd..914ff5a58dd59788c5bad43a2d0d7e2a1ee48450 100644 (file)
@@ -20,23 +20,6 @@ struct page **dma_common_find_pages(void *cpu_addr)
        return area->pages;
 }
 
-static struct vm_struct *__dma_common_pages_remap(struct page **pages,
-                       size_t size, pgprot_t prot, const void *caller)
-{
-       struct vm_struct *area;
-
-       area = get_vm_area_caller(size, VM_DMA_COHERENT, caller);
-       if (!area)
-               return NULL;
-
-       if (map_vm_area(area, prot, pages)) {
-               vunmap(area->addr);
-               return NULL;
-       }
-
-       return area;
-}
-
 /*
  * Remaps an array of PAGE_SIZE pages into another vm_area.
  * Cannot be used in non-sleeping contexts
@@ -44,15 +27,12 @@ static struct vm_struct *__dma_common_pages_remap(struct page **pages,
 void *dma_common_pages_remap(struct page **pages, size_t size,
                         pgprot_t prot, const void *caller)
 {
-       struct vm_struct *area;
+       void *vaddr;
 
-       area = __dma_common_pages_remap(pages, size, prot, caller);
-       if (!area)
-               return NULL;
-
-       area->pages = pages;
-
-       return area->addr;
+       vaddr = vmap(pages, size >> PAGE_SHIFT, VM_DMA_COHERENT, prot);
+       if (vaddr)
+               find_vm_area(vaddr)->pages = pages;
+       return vaddr;
 }
 
 /*
@@ -62,24 +42,20 @@ void *dma_common_pages_remap(struct page **pages, size_t size,
 void *dma_common_contiguous_remap(struct page *page, size_t size,
                        pgprot_t prot, const void *caller)
 {
-       int i;
+       int count = size >> PAGE_SHIFT;
        struct page **pages;
-       struct vm_struct *area;
+       void *vaddr;
+       int i;
 
-       pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
+       pages = kmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
        if (!pages)
                return NULL;
-
-       for (i = 0; i < (size >> PAGE_SHIFT); i++)
+       for (i = 0; i < count; i++)
                pages[i] = nth_page(page, i);
-
-       area = __dma_common_pages_remap(pages, size, prot, caller);
-
+       vaddr = vmap(pages, count, VM_DMA_COHERENT, prot);
        kfree(pages);
 
-       if (!area)
-               return NULL;
-       return area->addr;
+       return vaddr;
 }
 
 /*
index c2b41a263166c72e9c569ab4957aeb928e06dda9..b1991043b7d8495ae293f38dc92226cf67dbd8ac 100644 (file)
@@ -16,7 +16,7 @@
 
 struct callchain_cpus_entries {
        struct rcu_head                 rcu_head;
-       struct perf_callchain_entry     *cpu_entries[0];
+       struct perf_callchain_entry     *cpu_entries[];
 };
 
 int sysctl_perf_event_max_stack __read_mostly = PERF_MAX_STACK_DEPTH;
index 633b4ae72ed598449f3ba2bb0da24eb92e86b17e..e296c5c59c6f701b9d9bc3d0eb7cef8cd94aecfc 100644 (file)
@@ -95,11 +95,11 @@ static void remote_function(void *data)
  * @info:      the function call argument
  *
  * Calls the function @func when the task is currently running. This might
- * be on the current CPU, which just calls the function directly
+ * be on the current CPU, which just calls the function directly.  This will
+ * retry due to any failures in smp_call_function_single(), such as if the
+ * task_cpu() goes offline concurrently.
  *
- * returns: @func return value, or
- *         -ESRCH  - when the process isn't running
- *         -EAGAIN - when the process moved away
+ * returns @func return value or -ESRCH when the process isn't running
  */
 static int
 task_function_call(struct task_struct *p, remote_function_f func, void *info)
@@ -112,11 +112,16 @@ task_function_call(struct task_struct *p, remote_function_f func, void *info)
        };
        int ret;
 
-       do {
-               ret = smp_call_function_single(task_cpu(p), remote_function, &data, 1);
-               if (!ret)
-                       ret = data.ret;
-       } while (ret == -EAGAIN);
+       for (;;) {
+               ret = smp_call_function_single(task_cpu(p), remote_function,
+                                              &data, 1);
+               ret = !ret ? data.ret : -EAGAIN;
+
+               if (ret != -EAGAIN)
+                       break;
+
+               cond_resched();
+       }
 
        return ret;
 }
@@ -9404,7 +9409,7 @@ static int perf_kprobe_event_init(struct perf_event *event)
        if (event->attr.type != perf_kprobe.type)
                return -ENOENT;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EACCES;
 
        /*
@@ -9464,7 +9469,7 @@ static int perf_uprobe_event_init(struct perf_event *event)
        if (event->attr.type != perf_uprobe.type)
                return -ENOENT;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EACCES;
 
        /*
@@ -11511,7 +11516,7 @@ SYSCALL_DEFINE5(perf_event_open,
        }
 
        if (attr.namespaces) {
-               if (!capable(CAP_SYS_ADMIN))
+               if (!perfmon_capable())
                        return -EACCES;
        }
 
index f16f66b6b655ce5aac7c5635931b0f516fae5b8d..fcbf5616a441185c1669e18ba63a4fa7477ab525 100644 (file)
@@ -55,7 +55,7 @@ struct perf_buffer {
        void                            *aux_priv;
 
        struct perf_event_mmap_page     *user_page;
-       void                            *data_pages[0];
+       void                            *data_pages[];
 };
 
 extern void rb_free(struct perf_buffer *rb);
index ce2a75bc0adeb2bc7edd19e4df6680b272f62d99..1b772f2c671bc27236d4a6364d74bddb2fce7646 100644 (file)
@@ -1558,7 +1558,7 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        if (!infop)
                return err;
 
-       if (!user_access_begin(infop, sizeof(*infop)))
+       if (!user_write_access_begin(infop, sizeof(*infop)))
                return -EFAULT;
 
        unsafe_put_user(signo, &infop->si_signo, Efault);
@@ -1567,10 +1567,10 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        unsafe_put_user(info.pid, &infop->si_pid, Efault);
        unsafe_put_user(info.uid, &infop->si_uid, Efault);
        unsafe_put_user(info.status, &infop->si_status, Efault);
-       user_access_end();
+       user_write_access_end();
        return err;
 Efault:
-       user_access_end();
+       user_write_access_end();
        return -EFAULT;
 }
 
@@ -1685,7 +1685,7 @@ COMPAT_SYSCALL_DEFINE5(waitid,
        if (!infop)
                return err;
 
-       if (!user_access_begin(infop, sizeof(*infop)))
+       if (!user_write_access_begin(infop, sizeof(*infop)))
                return -EFAULT;
 
        unsafe_put_user(signo, &infop->si_signo, Efault);
@@ -1694,10 +1694,10 @@ COMPAT_SYSCALL_DEFINE5(waitid,
        unsafe_put_user(info.pid, &infop->si_pid, Efault);
        unsafe_put_user(info.uid, &infop->si_uid, Efault);
        unsafe_put_user(info.status, &infop->si_status, Efault);
-       user_access_end();
+       user_write_access_end();
        return err;
 Efault:
-       user_access_end();
+       user_write_access_end();
        return -EFAULT;
 }
 #endif
index 48ed22774efaa6c3d9bc7738d29cdb15d29ff02d..be98e94cb3cc884a3df514849be2bba4ef78e453 100644 (file)
@@ -94,6 +94,7 @@
 #include <linux/thread_info.h>
 #include <linux/stackleak.h>
 #include <linux/kasan.h>
+#include <linux/scs.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -456,6 +457,8 @@ void put_task_stack(struct task_struct *tsk)
 
 void free_task(struct task_struct *tsk)
 {
+       scs_release(tsk);
+
 #ifndef CONFIG_THREAD_INFO_IN_TASK
        /*
         * The task is finally done with both the stack and thread_info,
@@ -840,6 +843,8 @@ void __init fork_init(void)
                          NULL, free_vm_stack_cache);
 #endif
 
+       scs_init();
+
        lockdep_init_task(&init_task);
        uprobes_init();
 }
@@ -899,6 +904,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
        if (err)
                goto free_stack;
 
+       err = scs_prepare(tsk, node);
+       if (err)
+               goto free_stack;
+
 #ifdef CONFIG_SECCOMP
        /*
         * We must handle setting up seccomp filters once we're under
@@ -1683,6 +1692,11 @@ static inline void rcu_copy_process(struct task_struct *p)
        INIT_LIST_HEAD(&p->rcu_tasks_holdout_list);
        p->rcu_tasks_idle_cpu = -1;
 #endif /* #ifdef CONFIG_TASKS_RCU */
+#ifdef CONFIG_TASKS_TRACE_RCU
+       p->trc_reader_nesting = 0;
+       p->trc_reader_special.s = 0;
+       INIT_LIST_HEAD(&p->trc_holdout_list);
+#endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
 }
 
 struct pid *pidfd_pid(const struct file *file)
index b59532862bc0eb3406c8192aa9de2e131731785f..b4b9f960b6101df59208b26519b77472eb6aadba 100644 (file)
@@ -486,10 +486,13 @@ static u64 get_inode_sequence_number(struct inode *inode)
  * The key words are stored in @key on success.
  *
  * For shared mappings (when @fshared), the key is:
+ *
  *   ( inode->i_sequence, page->index, offset_within_page )
+ *
  * [ also see get_inode_sequence_number() ]
  *
  * For private mappings (or when !@fshared), the key is:
+ *
  *   ( current->mm, address, 0 )
  *
  * This allows (cross process, where applicable) identification of the futex
index daae2f2dc6d4f64565112f0d7fea9c984887ce26..6ee6691f6839f64554aceeb1bbeca0785feda08d 100644 (file)
@@ -20,7 +20,7 @@ struct group_info *groups_alloc(int gidsetsize)
        len = sizeof(struct group_info) + sizeof(kgid_t) * gidsetsize;
        gi = kmalloc(len, GFP_KERNEL_ACCOUNT|__GFP_NOWARN|__GFP_NORETRY);
        if (!gi)
-               gi = __vmalloc(len, GFP_KERNEL_ACCOUNT, PAGE_KERNEL);
+               gi = __vmalloc(len, GFP_KERNEL_ACCOUNT);
        if (!gi)
                return NULL;
 
index 2625c241ac00f81d5385be6024b9b3062c05ecfd..3f310df4a693eb7da189dc6b0f8f33e068fdebec 100644 (file)
@@ -2179,6 +2179,24 @@ int kprobe_add_area_blacklist(unsigned long start, unsigned long end)
        return 0;
 }
 
+/* Remove all symbols in given area from kprobe blacklist */
+static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end)
+{
+       struct kprobe_blacklist_entry *ent, *n;
+
+       list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) {
+               if (ent->start_addr < start || ent->start_addr >= end)
+                       continue;
+               list_del(&ent->list);
+               kfree(ent);
+       }
+}
+
+static void kprobe_remove_ksym_blacklist(unsigned long entry)
+{
+       kprobe_remove_area_blacklist(entry, entry + 1);
+}
+
 int __init __weak arch_populate_kprobe_blacklist(void)
 {
        return 0;
@@ -2211,10 +2229,62 @@ static int __init populate_kprobe_blacklist(unsigned long *start,
        /* Symbols in __kprobes_text are blacklisted */
        ret = kprobe_add_area_blacklist((unsigned long)__kprobes_text_start,
                                        (unsigned long)__kprobes_text_end);
+       if (ret)
+               return ret;
+
+       /* Symbols in noinstr section are blacklisted */
+       ret = kprobe_add_area_blacklist((unsigned long)__noinstr_text_start,
+                                       (unsigned long)__noinstr_text_end);
 
        return ret ? : arch_populate_kprobe_blacklist();
 }
 
+static void add_module_kprobe_blacklist(struct module *mod)
+{
+       unsigned long start, end;
+       int i;
+
+       if (mod->kprobe_blacklist) {
+               for (i = 0; i < mod->num_kprobe_blacklist; i++)
+                       kprobe_add_ksym_blacklist(mod->kprobe_blacklist[i]);
+       }
+
+       start = (unsigned long)mod->kprobes_text_start;
+       if (start) {
+               end = start + mod->kprobes_text_size;
+               kprobe_add_area_blacklist(start, end);
+       }
+
+       start = (unsigned long)mod->noinstr_text_start;
+       if (start) {
+               end = start + mod->noinstr_text_size;
+               kprobe_add_area_blacklist(start, end);
+       }
+}
+
+static void remove_module_kprobe_blacklist(struct module *mod)
+{
+       unsigned long start, end;
+       int i;
+
+       if (mod->kprobe_blacklist) {
+               for (i = 0; i < mod->num_kprobe_blacklist; i++)
+                       kprobe_remove_ksym_blacklist(mod->kprobe_blacklist[i]);
+       }
+
+       start = (unsigned long)mod->kprobes_text_start;
+       if (start) {
+               end = start + mod->kprobes_text_size;
+               kprobe_remove_area_blacklist(start, end);
+       }
+
+       start = (unsigned long)mod->noinstr_text_start;
+       if (start) {
+               end = start + mod->noinstr_text_size;
+               kprobe_remove_area_blacklist(start, end);
+       }
+}
+
 /* Module notifier call back, checking kprobes on the module */
 static int kprobes_module_callback(struct notifier_block *nb,
                                   unsigned long val, void *data)
@@ -2225,6 +2295,11 @@ static int kprobes_module_callback(struct notifier_block *nb,
        unsigned int i;
        int checkcore = (val == MODULE_STATE_GOING);
 
+       if (val == MODULE_STATE_COMING) {
+               mutex_lock(&kprobe_mutex);
+               add_module_kprobe_blacklist(mod);
+               mutex_unlock(&kprobe_mutex);
+       }
        if (val != MODULE_STATE_GOING && val != MODULE_STATE_LIVE)
                return NOTIFY_DONE;
 
@@ -2255,6 +2330,8 @@ static int kprobes_module_callback(struct notifier_block *nb,
                                kill_kprobe(p);
                        }
        }
+       if (val == MODULE_STATE_GOING)
+               remove_module_kprobe_blacklist(mod);
        mutex_unlock(&kprobe_mutex);
        return NOTIFY_DONE;
 }
@@ -2420,6 +2497,7 @@ static const struct file_operations debugfs_kprobes_operations = {
 /* kprobes/blacklist -- shows which functions can not be probed */
 static void *kprobe_blacklist_seq_start(struct seq_file *m, loff_t *pos)
 {
+       mutex_lock(&kprobe_mutex);
        return seq_list_start(&kprobe_blacklist, *pos);
 }
 
@@ -2446,10 +2524,15 @@ static int kprobe_blacklist_seq_show(struct seq_file *m, void *v)
        return 0;
 }
 
+static void kprobe_blacklist_seq_stop(struct seq_file *f, void *v)
+{
+       mutex_unlock(&kprobe_mutex);
+}
+
 static const struct seq_operations kprobe_blacklist_seq_ops = {
        .start = kprobe_blacklist_seq_start,
        .next  = kprobe_blacklist_seq_next,
-       .stop  = kprobe_seq_stop,       /* Reuse void function */
+       .stop  = kprobe_blacklist_seq_stop,
        .show  = kprobe_blacklist_seq_show,
 };
 
index ac10db66cc63f4b8f96ad59e89ac3caee18a2b6d..dd3cc0854c32ea1f78f59a9d2befbca2da5906c8 100644 (file)
@@ -393,25 +393,6 @@ void lockdep_init_task(struct task_struct *task)
        task->lockdep_recursion = 0;
 }
 
-/*
- * Split the recrursion counter in two to readily detect 'off' vs recursion.
- */
-#define LOCKDEP_RECURSION_BITS 16
-#define LOCKDEP_OFF            (1U << LOCKDEP_RECURSION_BITS)
-#define LOCKDEP_RECURSION_MASK (LOCKDEP_OFF - 1)
-
-void lockdep_off(void)
-{
-       current->lockdep_recursion += LOCKDEP_OFF;
-}
-EXPORT_SYMBOL(lockdep_off);
-
-void lockdep_on(void)
-{
-       current->lockdep_recursion -= LOCKDEP_OFF;
-}
-EXPORT_SYMBOL(lockdep_on);
-
 static inline void lockdep_recursion_finish(void)
 {
        if (WARN_ON_ONCE(--current->lockdep_recursion))
@@ -489,7 +470,7 @@ struct lock_trace {
        struct hlist_node       hash_entry;
        u32                     hash;
        u32                     nr_entries;
-       unsigned long           entries[0] __aligned(sizeof(unsigned long));
+       unsigned long           entries[] __aligned(sizeof(unsigned long));
 };
 #define LOCK_TRACE_SIZE_IN_LONGS                               \
        (sizeof(struct lock_trace) / sizeof(unsigned long))
index c9f090d64f0016b895ce4d25173923c8f01357b0..cfdd5b93264d7e17ca120aa3d5fafe139a2c33f9 100644 (file)
@@ -141,7 +141,6 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock)
  * set up.
  */
 #ifndef CONFIG_DEBUG_RT_MUTEXES
-# define rt_mutex_cmpxchg_relaxed(l,c,n) (cmpxchg_relaxed(&l->owner, c, n) == c)
 # define rt_mutex_cmpxchg_acquire(l,c,n) (cmpxchg_acquire(&l->owner, c, n) == c)
 # define rt_mutex_cmpxchg_release(l,c,n) (cmpxchg_release(&l->owner, c, n) == c)
 
@@ -202,7 +201,6 @@ static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock,
 }
 
 #else
-# define rt_mutex_cmpxchg_relaxed(l,c,n)       (0)
 # define rt_mutex_cmpxchg_acquire(l,c,n)       (0)
 # define rt_mutex_cmpxchg_release(l,c,n)       (0)
 
index 646f1e2330d2bdd9e3e82f62faff410d7d8ff37e..a0f201d2e18480d2af25ccf67a3e1a7374b8cefe 100644 (file)
@@ -2400,7 +2400,7 @@ static void layout_sections(struct module *mod, struct load_info *info)
                        if ((s->sh_flags & masks[m][0]) != masks[m][0]
                            || (s->sh_flags & masks[m][1])
                            || s->sh_entsize != ~0UL
-                           || strstarts(sname, ".init"))
+                           || module_init_section(sname))
                                continue;
                        s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i);
                        pr_debug("\t%s\n", sname);
@@ -2433,7 +2433,7 @@ static void layout_sections(struct module *mod, struct load_info *info)
                        if ((s->sh_flags & masks[m][0]) != masks[m][0]
                            || (s->sh_flags & masks[m][1])
                            || s->sh_entsize != ~0UL
-                           || !strstarts(sname, ".init"))
+                           || !module_init_section(sname))
                                continue;
                        s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i)
                                         | INIT_OFFSET_MASK);
@@ -2768,6 +2768,11 @@ void * __weak module_alloc(unsigned long size)
        return vmalloc_exec(size);
 }
 
+bool __weak module_init_section(const char *name)
+{
+       return strstarts(name, ".init");
+}
+
 bool __weak module_exit_section(const char *name)
 {
        return strstarts(name, ".exit");
@@ -2946,8 +2951,7 @@ static int copy_module_from_user(const void __user *umod, unsigned long len,
                return err;
 
        /* Suck in entire file: we'll want most of it. */
-       info->hdr = __vmalloc(info->len,
-                       GFP_KERNEL | __GFP_NOWARN, PAGE_KERNEL);
+       info->hdr = __vmalloc(info->len, GFP_KERNEL | __GFP_NOWARN);
        if (!info->hdr)
                return -ENOMEM;
 
@@ -3150,6 +3154,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
        }
 #endif
 
+       mod->noinstr_text_start = section_objs(info, ".noinstr.text", 1,
+                                               &mod->noinstr_text_size);
+
 #ifdef CONFIG_TRACEPOINTS
        mod->tracepoints_ptrs = section_objs(info, "__tracepoints_ptrs",
                                             sizeof(*mod->tracepoints_ptrs),
@@ -3193,6 +3200,13 @@ static int find_module_sections(struct module *mod, struct load_info *info)
        mod->ei_funcs = section_objs(info, "_error_injection_whitelist",
                                            sizeof(*mod->ei_funcs),
                                            &mod->num_ei_funcs);
+#endif
+#ifdef CONFIG_KPROBES
+       mod->kprobes_text_start = section_objs(info, ".kprobes.text", 1,
+                                               &mod->kprobes_text_size);
+       mod->kprobe_blacklist = section_objs(info, "_kprobe_blacklist",
+                                               sizeof(unsigned long),
+                                               &mod->num_kprobe_blacklist);
 #endif
        mod->extable = section_objs(info, "__ex_table",
                                    sizeof(*mod->extable), &mod->num_exentries);
index 5989bbb93039e61da64a4b260c10b86f4e7349fd..84c987dfbe0360a406d79a0036c1d82dc35f5d53 100644 (file)
@@ -519,7 +519,6 @@ NOKPROBE_SYMBOL(notify_die);
 
 int register_die_notifier(struct notifier_block *nb)
 {
-       vmalloc_sync_mappings();
        return atomic_notifier_chain_register(&die_chain, nb);
 }
 EXPORT_SYMBOL_GPL(register_die_notifier);
index a6afa12fb75ee9dc29d01830898940568caa8e67..aae78989661651add7f950d63797a35f1822c974 100644 (file)
@@ -703,7 +703,7 @@ static int padata_cpu_online(unsigned int cpu, struct hlist_node *node)
        struct padata_instance *pinst;
        int ret;
 
-       pinst = hlist_entry_safe(node, struct padata_instance, node);
+       pinst = hlist_entry_safe(node, struct padata_instance, cpu_online_node);
        if (!pinst_has_cpu(pinst, cpu))
                return 0;
 
@@ -718,7 +718,7 @@ static int padata_cpu_dead(unsigned int cpu, struct hlist_node *node)
        struct padata_instance *pinst;
        int ret;
 
-       pinst = hlist_entry_safe(node, struct padata_instance, node);
+       pinst = hlist_entry_safe(node, struct padata_instance, cpu_dead_node);
        if (!pinst_has_cpu(pinst, cpu))
                return 0;
 
@@ -734,8 +734,9 @@ static enum cpuhp_state hp_online;
 static void __padata_free(struct padata_instance *pinst)
 {
 #ifdef CONFIG_HOTPLUG_CPU
-       cpuhp_state_remove_instance_nocalls(CPUHP_PADATA_DEAD, &pinst->node);
-       cpuhp_state_remove_instance_nocalls(hp_online, &pinst->node);
+       cpuhp_state_remove_instance_nocalls(CPUHP_PADATA_DEAD,
+                                           &pinst->cpu_dead_node);
+       cpuhp_state_remove_instance_nocalls(hp_online, &pinst->cpu_online_node);
 #endif
 
        WARN_ON(!list_empty(&pinst->pslist));
@@ -939,9 +940,10 @@ static struct padata_instance *padata_alloc(const char *name,
        mutex_init(&pinst->lock);
 
 #ifdef CONFIG_HOTPLUG_CPU
-       cpuhp_state_add_instance_nocalls_cpuslocked(hp_online, &pinst->node);
+       cpuhp_state_add_instance_nocalls_cpuslocked(hp_online,
+                                                   &pinst->cpu_online_node);
        cpuhp_state_add_instance_nocalls_cpuslocked(CPUHP_PADATA_DEAD,
-                                                   &pinst->node);
+                                                   &pinst->cpu_dead_node);
 #endif
 
        put_online_cpus();
index c208566c844b09a6c04c6cebfcb87400d5a65af0..4d0e6e815a2be145beabda317fc7097b0ac878fb 100644 (file)
@@ -80,6 +80,18 @@ config HIBERNATION
 
          For more information take a look at <file:Documentation/power/swsusp.rst>.
 
+config HIBERNATION_SNAPSHOT_DEV
+       bool "Userspace snapshot device"
+       depends on HIBERNATION
+       default y
+       ---help---
+         Device used by the uswsusp tools.
+
+         Say N if no snapshotting from userspace is needed, this also
+         reduces the attack surface of the kernel.
+
+         If in doubt, say Y.
+
 config PM_STD_PARTITION
        string "Default resume partition"
        depends on HIBERNATION
index e7e47d9be1e56108e8f57bebad7760dcb173dbf1..5899260a8bef4b24f4292aa555b201e1147b9b3c 100644 (file)
@@ -10,7 +10,8 @@ obj-$(CONFIG_VT_CONSOLE_SLEEP)        += console.o
 obj-$(CONFIG_FREEZER)          += process.o
 obj-$(CONFIG_SUSPEND)          += suspend.o
 obj-$(CONFIG_PM_TEST_SUSPEND)  += suspend_test.o
-obj-$(CONFIG_HIBERNATION)      += hibernate.o snapshot.o swap.o user.o
+obj-$(CONFIG_HIBERNATION)      += hibernate.o snapshot.o swap.o
+obj-$(CONFIG_HIBERNATION_SNAPSHOT_DEV) += user.o
 obj-$(CONFIG_PM_AUTOSLEEP)     += autosleep.o
 obj-$(CONFIG_PM_WAKELOCKS)     += wakelock.o
 
index 30bd28d1d418cce6d785d1b65e68f429a253845d..02ec716a492713877caa67ff51b2d83a5d495b3e 100644 (file)
@@ -67,6 +67,18 @@ bool freezer_test_done;
 
 static const struct platform_hibernation_ops *hibernation_ops;
 
+static atomic_t hibernate_atomic = ATOMIC_INIT(1);
+
+bool hibernate_acquire(void)
+{
+       return atomic_add_unless(&hibernate_atomic, -1, 0);
+}
+
+void hibernate_release(void)
+{
+       atomic_inc(&hibernate_atomic);
+}
+
 bool hibernation_available(void)
 {
        return nohibernate == 0 && !security_locked_down(LOCKDOWN_HIBERNATION);
@@ -704,7 +716,7 @@ int hibernate(void)
 
        lock_system_sleep();
        /* The snapshot device should not be opened while we're running */
-       if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
+       if (!hibernate_acquire()) {
                error = -EBUSY;
                goto Unlock;
        }
@@ -775,7 +787,7 @@ int hibernate(void)
  Exit:
        __pm_notifier_call_chain(PM_POST_HIBERNATION, nr_calls, NULL);
        pm_restore_console();
-       atomic_inc(&snapshot_device_available);
+       hibernate_release();
  Unlock:
        unlock_system_sleep();
        pr_info("hibernation exit\n");
@@ -880,7 +892,7 @@ static int software_resume(void)
                goto Unlock;
 
        /* The snapshot device should not be opened while we're running */
-       if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
+       if (!hibernate_acquire()) {
                error = -EBUSY;
                swsusp_close(FMODE_READ);
                goto Unlock;
@@ -911,7 +923,7 @@ static int software_resume(void)
        __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
        pm_restore_console();
        pr_info("resume failed (%d)\n", error);
-       atomic_inc(&snapshot_device_available);
+       hibernate_release();
        /* For success case, the suspend path will release the lock */
  Unlock:
        mutex_unlock(&system_transition_mutex);
index 7cdc64dc237312a109aeb0b49f0e62524c183ce7..ba2094db62949446f5fca3e4f46edbc559f1cce3 100644 (file)
@@ -154,8 +154,8 @@ extern int snapshot_write_next(struct snapshot_handle *handle);
 extern void snapshot_write_finalize(struct snapshot_handle *handle);
 extern int snapshot_image_loaded(struct snapshot_handle *handle);
 
-/* If unset, the snapshot device cannot be open. */
-extern atomic_t snapshot_device_available;
+extern bool hibernate_acquire(void);
+extern void hibernate_release(void);
 
 extern sector_t alloc_swapdev_block(int swap);
 extern void free_all_swap_pages(int swap);
index 7959449765d9a557822d6932e8471c02daf5653c..d5eedc2baa2a10afb2382644f58106e35de0a103 100644 (file)
@@ -35,9 +35,13 @@ static struct snapshot_data {
        bool ready;
        bool platform_support;
        bool free_bitmaps;
+       struct inode *bd_inode;
 } snapshot_state;
 
-atomic_t snapshot_device_available = ATOMIC_INIT(1);
+int is_hibernate_resume_dev(const struct inode *bd_inode)
+{
+       return hibernation_available() && snapshot_state.bd_inode == bd_inode;
+}
 
 static int snapshot_open(struct inode *inode, struct file *filp)
 {
@@ -49,13 +53,13 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 
        lock_system_sleep();
 
-       if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
+       if (!hibernate_acquire()) {
                error = -EBUSY;
                goto Unlock;
        }
 
        if ((filp->f_flags & O_ACCMODE) == O_RDWR) {
-               atomic_inc(&snapshot_device_available);
+               hibernate_release();
                error = -ENOSYS;
                goto Unlock;
        }
@@ -92,11 +96,12 @@ static int snapshot_open(struct inode *inode, struct file *filp)
                        __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL);
        }
        if (error)
-               atomic_inc(&snapshot_device_available);
+               hibernate_release();
 
        data->frozen = false;
        data->ready = false;
        data->platform_support = false;
+       data->bd_inode = NULL;
 
  Unlock:
        unlock_system_sleep();
@@ -112,6 +117,7 @@ static int snapshot_release(struct inode *inode, struct file *filp)
 
        swsusp_free();
        data = filp->private_data;
+       data->bd_inode = NULL;
        free_all_swap_pages(data->swap);
        if (data->frozen) {
                pm_restore_gfp_mask();
@@ -122,7 +128,7 @@ static int snapshot_release(struct inode *inode, struct file *filp)
        }
        pm_notifier_call_chain(data->mode == O_RDONLY ?
                        PM_POST_HIBERNATION : PM_POST_RESTORE);
-       atomic_inc(&snapshot_device_available);
+       hibernate_release();
 
        unlock_system_sleep();
 
@@ -204,6 +210,7 @@ struct compat_resume_swap_area {
 static int snapshot_set_swap_area(struct snapshot_data *data,
                void __user *argp)
 {
+       struct block_device *bdev;
        sector_t offset;
        dev_t swdev;
 
@@ -234,9 +241,12 @@ static int snapshot_set_swap_area(struct snapshot_data *data,
                data->swap = -1;
                return -EINVAL;
        }
-       data->swap = swap_type_of(swdev, offset, NULL);
+       data->swap = swap_type_of(swdev, offset, &bdev);
        if (data->swap < 0)
                return -ENODEV;
+
+       data->bd_inode = bdev->bd_inode;
+       bdput(bdev);
        return 0;
 }
 
index 11f19c466af56c97de893ab14d1e01ea579cada2..3ca74ad391d6e88daf0d63f3085a7991daad361f 100644 (file)
@@ -6,6 +6,7 @@ struct console_cmdline
 {
        char    name[16];                       /* Name of the driver       */
        int     index;                          /* Minor dev. to use        */
+       bool    user_specified;                 /* Specified by command line vs. platform */
        char    *options;                       /* Options for the driver   */
 #ifdef CONFIG_A11Y_BRAILLE_CONSOLE
        char    *brl_options;                   /* Options for braille driver */
index b2b0f526f249e7edbbe11616ffe828899ce4f983..660f9a6bf73a1404870085ab03959f59baccf2c6 100644 (file)
@@ -6,9 +6,11 @@
 
 #ifdef CONFIG_PRINTK
 
-#define PRINTK_SAFE_CONTEXT_MASK        0x3fffffff
-#define PRINTK_NMI_DIRECT_CONTEXT_MASK  0x40000000
-#define PRINTK_NMI_CONTEXT_MASK                 0x80000000
+#define PRINTK_SAFE_CONTEXT_MASK       0x007ffffff
+#define PRINTK_NMI_DIRECT_CONTEXT_MASK 0x008000000
+#define PRINTK_NMI_CONTEXT_MASK                0xff0000000
+
+#define PRINTK_NMI_CONTEXT_OFFSET      0x010000000
 
 extern raw_spinlock_t logbuf_lock;
 
index 9a9b6156270bb755a16c5601d2473c5b58a0c896..9fdd6a42ad6ae51929ac2a948c81332c1a7c177c 100644 (file)
@@ -280,6 +280,7 @@ static struct console *exclusive_console;
 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
 
 static int preferred_console = -1;
+static bool has_preferred_console;
 int console_set_on_cmdline;
 EXPORT_SYMBOL(console_set_on_cmdline);
 
@@ -974,6 +975,16 @@ static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
                user->idx = log_next_idx;
                user->seq = log_next_seq;
                break;
+       case SEEK_CUR:
+               /*
+                * It isn't supported due to the record nature of this
+                * interface: _SET _DATA and _END point to very specific
+                * record positions, while _CUR would be more useful in case
+                * of a byte-based log. Because of that, return the default
+                * errno value for invalid seek operation.
+                */
+               ret = -ESPIPE;
+               break;
        default:
                ret = -EINVAL;
        }
@@ -2140,7 +2151,7 @@ asmlinkage __visible void early_printk(const char *fmt, ...)
 #endif
 
 static int __add_preferred_console(char *name, int idx, char *options,
-                                  char *brl_options)
+                                  char *brl_options, bool user_specified)
 {
        struct console_cmdline *c;
        int i;
@@ -2155,6 +2166,8 @@ static int __add_preferred_console(char *name, int idx, char *options,
                if (strcmp(c->name, name) == 0 && c->index == idx) {
                        if (!brl_options)
                                preferred_console = i;
+                       if (user_specified)
+                               c->user_specified = true;
                        return 0;
                }
        }
@@ -2164,6 +2177,7 @@ static int __add_preferred_console(char *name, int idx, char *options,
                preferred_console = i;
        strlcpy(c->name, name, sizeof(c->name));
        c->options = options;
+       c->user_specified = user_specified;
        braille_set_options(c, brl_options);
 
        c->index = idx;
@@ -2190,6 +2204,9 @@ static int __init console_setup(char *str)
        char *s, *options, *brl_options = NULL;
        int idx;
 
+       if (str[0] == 0)
+               return 1;
+
        if (_braille_console_setup(&str, &brl_options))
                return 1;
 
@@ -2218,7 +2235,7 @@ static int __init console_setup(char *str)
        idx = simple_strtoul(s, NULL, 10);
        *s = 0;
 
-       __add_preferred_console(buf, idx, options, brl_options);
+       __add_preferred_console(buf, idx, options, brl_options, true);
        console_set_on_cmdline = 1;
        return 1;
 }
@@ -2239,7 +2256,7 @@ __setup("console=", console_setup);
  */
 int add_preferred_console(char *name, int idx, char *options)
 {
-       return __add_preferred_console(name, idx, options, NULL);
+       return __add_preferred_console(name, idx, options, NULL, false);
 }
 
 bool console_suspend_enabled = true;
@@ -2438,9 +2455,9 @@ again:
                printk_safe_enter_irqsave(flags);
                raw_spin_lock(&logbuf_lock);
                if (console_seq < log_first_seq) {
-                       len = sprintf(text,
-                                     "** %llu printk messages dropped **\n",
-                                     log_first_seq - console_seq);
+                       len = snprintf(text, sizeof(text),
+                                      "** %llu printk messages dropped **\n",
+                                      log_first_seq - console_seq);
 
                        /* messages are gone, move to first one */
                        console_seq = log_first_seq;
@@ -2651,6 +2668,63 @@ static int __init keep_bootcon_setup(char *str)
 
 early_param("keep_bootcon", keep_bootcon_setup);
 
+/*
+ * This is called by register_console() to try to match
+ * the newly registered console with any of the ones selected
+ * by either the command line or add_preferred_console() and
+ * setup/enable it.
+ *
+ * Care need to be taken with consoles that are statically
+ * enabled such as netconsole
+ */
+static int try_enable_new_console(struct console *newcon, bool user_specified)
+{
+       struct console_cmdline *c;
+       int i;
+
+       for (i = 0, c = console_cmdline;
+            i < MAX_CMDLINECONSOLES && c->name[0];
+            i++, c++) {
+               if (c->user_specified != user_specified)
+                       continue;
+               if (!newcon->match ||
+                   newcon->match(newcon, c->name, c->index, c->options) != 0) {
+                       /* default matching */
+                       BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name));
+                       if (strcmp(c->name, newcon->name) != 0)
+                               continue;
+                       if (newcon->index >= 0 &&
+                           newcon->index != c->index)
+                               continue;
+                       if (newcon->index < 0)
+                               newcon->index = c->index;
+
+                       if (_braille_register_console(newcon, c))
+                               return 0;
+
+                       if (newcon->setup &&
+                           newcon->setup(newcon, c->options) != 0)
+                               return -EIO;
+               }
+               newcon->flags |= CON_ENABLED;
+               if (i == preferred_console) {
+                       newcon->flags |= CON_CONSDEV;
+                       has_preferred_console = true;
+               }
+               return 0;
+       }
+
+       /*
+        * Some consoles, such as pstore and netconsole, can be enabled even
+        * without matching. Accept the pre-enabled consoles only when match()
+        * and setup() had a change to be called.
+        */
+       if (newcon->flags & CON_ENABLED && c->user_specified == user_specified)
+               return 0;
+
+       return -ENOENT;
+}
+
 /*
  * The console driver calls this routine during kernel initialization
  * to register the console printing procedure with printk() and to
@@ -2672,11 +2746,9 @@ early_param("keep_bootcon", keep_bootcon_setup);
  */
 void register_console(struct console *newcon)
 {
-       int i;
        unsigned long flags;
        struct console *bcon = NULL;
-       struct console_cmdline *c;
-       static bool has_preferred;
+       int err;
 
        for_each_console(bcon) {
                if (WARN(bcon == newcon, "console '%s%d' already registered\n",
@@ -2701,15 +2773,15 @@ void register_console(struct console *newcon)
        if (console_drivers && console_drivers->flags & CON_BOOT)
                bcon = console_drivers;
 
-       if (!has_preferred || bcon || !console_drivers)
-               has_preferred = preferred_console >= 0;
+       if (!has_preferred_console || bcon || !console_drivers)
+               has_preferred_console = preferred_console >= 0;
 
        /*
         *      See if we want to use this console driver. If we
         *      didn't select a console we take the first one
         *      that registers here.
         */
-       if (!has_preferred) {
+       if (!has_preferred_console) {
                if (newcon->index < 0)
                        newcon->index = 0;
                if (newcon->setup == NULL ||
@@ -2717,47 +2789,20 @@ void register_console(struct console *newcon)
                        newcon->flags |= CON_ENABLED;
                        if (newcon->device) {
                                newcon->flags |= CON_CONSDEV;
-                               has_preferred = true;
+                               has_preferred_console = true;
                        }
                }
        }
 
-       /*
-        *      See if this console matches one we selected on
-        *      the command line.
-        */
-       for (i = 0, c = console_cmdline;
-            i < MAX_CMDLINECONSOLES && c->name[0];
-            i++, c++) {
-               if (!newcon->match ||
-                   newcon->match(newcon, c->name, c->index, c->options) != 0) {
-                       /* default matching */
-                       BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name));
-                       if (strcmp(c->name, newcon->name) != 0)
-                               continue;
-                       if (newcon->index >= 0 &&
-                           newcon->index != c->index)
-                               continue;
-                       if (newcon->index < 0)
-                               newcon->index = c->index;
-
-                       if (_braille_register_console(newcon, c))
-                               return;
-
-                       if (newcon->setup &&
-                           newcon->setup(newcon, c->options) != 0)
-                               break;
-               }
+       /* See if this console matches one we selected on the command line */
+       err = try_enable_new_console(newcon, true);
 
-               newcon->flags |= CON_ENABLED;
-               if (i == preferred_console) {
-                       newcon->flags |= CON_CONSDEV;
-                       has_preferred = true;
-               }
-               break;
-       }
+       /* If not, try to match against the platform default(s) */
+       if (err == -ENOENT)
+               err = try_enable_new_console(newcon, false);
 
-       if (!(newcon->flags & CON_ENABLED))
+       /* printk() messages are not printed to the Braille console. */
+       if (err || newcon->flags & CON_BRL)
                return;
 
        /*
@@ -2779,6 +2824,8 @@ void register_console(struct console *newcon)
                console_drivers = newcon;
                if (newcon->next)
                        newcon->next->flags &= ~CON_CONSDEV;
+               /* Ensure this flag is always set for the head of the list */
+               newcon->flags |= CON_CONSDEV;
        } else {
                newcon->next = console_drivers->next;
                console_drivers->next = newcon;
@@ -3144,6 +3191,23 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
 static bool always_kmsg_dump;
 module_param_named(always_kmsg_dump, always_kmsg_dump, bool, S_IRUGO | S_IWUSR);
 
+const char *kmsg_dump_reason_str(enum kmsg_dump_reason reason)
+{
+       switch (reason) {
+       case KMSG_DUMP_PANIC:
+               return "Panic";
+       case KMSG_DUMP_OOPS:
+               return "Oops";
+       case KMSG_DUMP_EMERG:
+               return "Emergency";
+       case KMSG_DUMP_SHUTDOWN:
+               return "Shutdown";
+       default:
+               return "Unknown";
+       }
+}
+EXPORT_SYMBOL_GPL(kmsg_dump_reason_str);
+
 /**
  * kmsg_dump - dump kernel log to kernel message dumpers.
  * @reason: the reason (oops, panic etc) for dumping
@@ -3157,12 +3221,19 @@ void kmsg_dump(enum kmsg_dump_reason reason)
        struct kmsg_dumper *dumper;
        unsigned long flags;
 
-       if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
-               return;
-
        rcu_read_lock();
        list_for_each_entry_rcu(dumper, &dump_list, list) {
-               if (dumper->max_reason && reason > dumper->max_reason)
+               enum kmsg_dump_reason max_reason = dumper->max_reason;
+
+               /*
+                * If client has not provided a specific max_reason, default
+                * to KMSG_DUMP_OOPS, unless always_kmsg_dump was set.
+                */
+               if (max_reason == KMSG_DUMP_UNDEF) {
+                       max_reason = always_kmsg_dump ? KMSG_DUMP_MAX :
+                                                       KMSG_DUMP_OOPS;
+               }
+               if (reason > max_reason)
                        continue;
 
                /* initialize iterator with data about the stored records */
@@ -3360,7 +3431,7 @@ out:
 EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
 
 /**
- * kmsg_dump_rewind_nolock - reset the interator (unlocked version)
+ * kmsg_dump_rewind_nolock - reset the iterator (unlocked version)
  * @dumper: registered kmsg dumper
  *
  * Reset the dumper's iterator so that kmsg_dump_get_line() and
@@ -3378,7 +3449,7 @@ void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
 }
 
 /**
- * kmsg_dump_rewind - reset the interator
+ * kmsg_dump_rewind - reset the iterator
  * @dumper: registered kmsg dumper
  *
  * Reset the dumper's iterator so that kmsg_dump_get_line() and
index d9a659a686f31dcf4842063e914001b592e0b8ba..4242403316bba12d0ec7691d48ece1d55be99a20 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/cpumask.h>
 #include <linux/irq_work.h>
 #include <linux/printk.h>
+#include <linux/kprobes.h>
 
 #include "internal.h"
 
@@ -293,14 +294,14 @@ static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
        return printk_safe_log_store(s, fmt, args);
 }
 
-void notrace printk_nmi_enter(void)
+void noinstr printk_nmi_enter(void)
 {
-       this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
+       this_cpu_add(printk_context, PRINTK_NMI_CONTEXT_OFFSET);
 }
 
-void notrace printk_nmi_exit(void)
+void noinstr printk_nmi_exit(void)
 {
-       this_cpu_and(printk_context, ~PRINTK_NMI_CONTEXT_MASK);
+       this_cpu_sub(printk_context, PRINTK_NMI_CONTEXT_OFFSET);
 }
 
 /*
index 1cc940fef17c39c982af652cd45aaf342afcee9e..0ebe15a849856bf31604485966d21bdeca807d8a 100644 (file)
@@ -70,13 +70,37 @@ config TREE_SRCU
        help
          This option selects the full-fledged version of SRCU.
 
+config TASKS_RCU_GENERIC
+       def_bool TASKS_RCU || TASKS_RUDE_RCU || TASKS_TRACE_RCU
+       select SRCU
+       help
+         This option enables generic infrastructure code supporting
+         task-based RCU implementations.  Not for manual selection.
+
 config TASKS_RCU
        def_bool PREEMPTION
-       select SRCU
        help
          This option enables a task-based RCU implementation that uses
          only voluntary context switch (not preemption!), idle, and
-         user-mode execution as quiescent states.
+         user-mode execution as quiescent states.  Not for manual selection.
+
+config TASKS_RUDE_RCU
+       def_bool 0
+       help
+         This option enables a task-based RCU implementation that uses
+         only context switch (including preemption) and user-mode
+         execution as quiescent states.  It forces IPIs and context
+         switches on all online CPUs, including idle ones, so use
+         with caution.
+
+config TASKS_TRACE_RCU
+       def_bool 0
+       help
+         This option enables a task-based RCU implementation that uses
+         explicit rcu_read_lock_trace() read-side markers, and allows
+         these readers to appear in the idle loop as well as on the CPU
+         hotplug code paths.  It can force IPIs on online CPUs, including
+         idle ones, so use with caution.
 
 config RCU_STALL_COMMON
        def_bool TREE_RCU
@@ -210,4 +234,22 @@ config RCU_NOCB_CPU
          Say Y here if you want to help to debug reduced OS jitter.
          Say N here if you are unsure.
 
+config TASKS_TRACE_RCU_READ_MB
+       bool "Tasks Trace RCU readers use memory barriers in user and idle"
+       depends on RCU_EXPERT
+       default PREEMPT_RT || NR_CPUS < 8
+       help
+         Use this option to further reduce the number of IPIs sent
+         to CPUs executing in userspace or idle during tasks trace
+         RCU grace periods.  Given that a reasonable setting of
+         the rcupdate.rcu_task_ipi_delay kernel boot parameter
+         eliminates such IPIs for many workloads, proper setting
+         of this Kconfig option is important mostly for aggressive
+         real-time installations and for battery-powered devices,
+         hence the default chosen above.
+
+         Say Y here if you hate IPIs.
+         Say N here if you hate read-side memory barriers.
+         Take the default if you are unsure.
+
 endmenu # "RCU Subsystem"
index 4aa02eee8f6c431d2b2ff6cf27675d3812f44709..452feae8de20c461f25f043a1358fa6513d87c19 100644 (file)
@@ -29,6 +29,8 @@ config RCU_PERF_TEST
        select TORTURE_TEST
        select SRCU
        select TASKS_RCU
+       select TASKS_RUDE_RCU
+       select TASKS_TRACE_RCU
        default n
        help
          This option provides a kernel module that runs performance
@@ -46,6 +48,8 @@ config RCU_TORTURE_TEST
        select TORTURE_TEST
        select SRCU
        select TASKS_RCU
+       select TASKS_RUDE_RCU
+       select TASKS_TRACE_RCU
        default n
        help
          This option provides a kernel module that runs torture tests
index 00ddc92c577428bf5b803aa9a09cbb7d4d5def1c..cf66a3ccd757326a20414f6c0cd59c4a8fb9a4d0 100644 (file)
@@ -431,6 +431,7 @@ bool rcu_gp_is_expedited(void);  /* Internal RCU use. */
 void rcu_expedite_gp(void);
 void rcu_unexpedite_gp(void);
 void rcupdate_announce_bootup_oddness(void);
+void show_rcu_tasks_gp_kthreads(void);
 void rcu_request_urgent_qs_task(struct task_struct *t);
 #endif /* #else #ifdef CONFIG_TINY_RCU */
 
@@ -441,6 +442,8 @@ void rcu_request_urgent_qs_task(struct task_struct *t);
 enum rcutorture_type {
        RCU_FLAVOR,
        RCU_TASKS_FLAVOR,
+       RCU_TASKS_RUDE_FLAVOR,
+       RCU_TASKS_TRACING_FLAVOR,
        RCU_TRIVIAL_FLAVOR,
        SRCU_FLAVOR,
        INVALID_RCU_FLAVOR
@@ -454,6 +457,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
                               unsigned long secs,
                               unsigned long c_old,
                               unsigned long c);
+void rcu_gp_set_torture_wait(int duration);
 #else
 static inline void rcutorture_get_gp_data(enum rcutorture_type test_type,
                                          int *flags, unsigned long *gp_seq)
@@ -471,6 +475,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
 #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
        do { } while (0)
 #endif
+static inline void rcu_gp_set_torture_wait(int duration) { }
 #endif
 
 #if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
@@ -498,6 +503,7 @@ void srcutorture_get_gp_data(enum rcutorture_type test_type,
 #endif
 
 #ifdef CONFIG_TINY_RCU
+static inline bool rcu_dynticks_zero_in_eqs(int cpu, int *vp) { return false; }
 static inline unsigned long rcu_get_gp_seq(void) { return 0; }
 static inline unsigned long rcu_exp_batches_completed(void) { return 0; }
 static inline unsigned long
@@ -507,6 +513,7 @@ static inline void show_rcu_gp_kthreads(void) { }
 static inline int rcu_get_gp_kthreads_prio(void) { return 0; }
 static inline void rcu_fwd_progress_check(unsigned long j) { }
 #else /* #ifdef CONFIG_TINY_RCU */
+bool rcu_dynticks_zero_in_eqs(int cpu, int *vp);
 unsigned long rcu_get_gp_seq(void);
 unsigned long rcu_exp_batches_completed(void);
 unsigned long srcu_batches_completed(struct srcu_struct *sp);
index a4a8d097d84d95fbd6ba38718aed356e2ceb4d85..16dd1e6b7c09fbd228cf7f745e2fe9308a1754a1 100644 (file)
@@ -88,6 +88,7 @@ torture_param(bool, shutdown, RCUPERF_SHUTDOWN,
 torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
 torture_param(int, writer_holdoff, 0, "Holdoff (us) between GPs, zero to disable");
 torture_param(int, kfree_rcu_test, 0, "Do we run a kfree_rcu() perf test?");
+torture_param(int, kfree_mult, 1, "Multiple of kfree_obj size to allocate.");
 
 static char *perf_type = "rcu";
 module_param(perf_type, charp, 0444);
@@ -635,7 +636,7 @@ kfree_perf_thread(void *arg)
                }
 
                for (i = 0; i < kfree_alloc_num; i++) {
-                       alloc_ptr = kmalloc(sizeof(struct kfree_obj), GFP_KERNEL);
+                       alloc_ptr = kmalloc(kfree_mult * sizeof(struct kfree_obj), GFP_KERNEL);
                        if (!alloc_ptr)
                                return -ENOMEM;
 
@@ -722,6 +723,8 @@ kfree_perf_init(void)
                schedule_timeout_uninterruptible(1);
        }
 
+       pr_alert("kfree object size=%lu\n", kfree_mult * sizeof(struct kfree_obj));
+
        kfree_reader_tasks = kcalloc(kfree_nrealthreads, sizeof(kfree_reader_tasks[0]),
                               GFP_KERNEL);
        if (kfree_reader_tasks == NULL) {
index 5453bd557f439c5ddefc98b1d4193a22443023f2..efb792e13fca91eb0dedf6fb2d6e4db40b77aec9 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/err.h>
 #include <linux/spinlock.h>
 #include <linux/smp.h>
-#include <linux/rcupdate.h>
+#include <linux/rcupdate_wait.h>
 #include <linux/interrupt.h>
 #include <linux/sched/signal.h>
 #include <uapi/linux/sched/types.h>
 #include <linux/sched/sysctl.h>
 #include <linux/oom.h>
 #include <linux/tick.h>
+#include <linux/rcupdate_trace.h>
 
 #include "rcu.h"
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com> and Josh Triplett <josh@joshtriplett.org>");
 
+#ifndef data_race
+#define data_race(expr)                                                        \
+       ({                                                              \
+               expr;                                                   \
+       })
+#endif
+#ifndef ASSERT_EXCLUSIVE_WRITER
+#define ASSERT_EXCLUSIVE_WRITER(var) do { } while (0)
+#endif
+#ifndef ASSERT_EXCLUSIVE_ACCESS
+#define ASSERT_EXCLUSIVE_ACCESS(var) do { } while (0)
+#endif
 
 /* Bits for ->extendables field, extendables param, and related definitions. */
 #define RCUTORTURE_RDR_SHIFT    8      /* Put SRCU index in upper bits. */
@@ -102,6 +115,9 @@ torture_param(int, stall_cpu, 0, "Stall duration (s), zero to disable.");
 torture_param(int, stall_cpu_holdoff, 10,
             "Time to wait before starting stall (s).");
 torture_param(int, stall_cpu_irqsoff, 0, "Disable interrupts while stalling.");
+torture_param(int, stall_cpu_block, 0, "Sleep while stalling.");
+torture_param(int, stall_gp_kthread, 0,
+             "Grace-period kthread stall duration (s).");
 torture_param(int, stat_interval, 60,
             "Number of seconds between stats printk()s");
 torture_param(int, stutter, 5, "Number of seconds to run/halt test");
@@ -665,6 +681,11 @@ static void rcu_tasks_torture_deferred_free(struct rcu_torture *p)
        call_rcu_tasks(&p->rtort_rcu, rcu_torture_cb);
 }
 
+static void synchronize_rcu_mult_test(void)
+{
+       synchronize_rcu_mult(call_rcu_tasks, call_rcu);
+}
+
 static struct rcu_torture_ops tasks_ops = {
        .ttype          = RCU_TASKS_FLAVOR,
        .init           = rcu_sync_torture_init,
@@ -674,7 +695,7 @@ static struct rcu_torture_ops tasks_ops = {
        .get_gp_seq     = rcu_no_completed,
        .deferred_free  = rcu_tasks_torture_deferred_free,
        .sync           = synchronize_rcu_tasks,
-       .exp_sync       = synchronize_rcu_tasks,
+       .exp_sync       = synchronize_rcu_mult_test,
        .call           = call_rcu_tasks,
        .cb_barrier     = rcu_barrier_tasks,
        .fqs            = NULL,
@@ -725,6 +746,72 @@ static struct rcu_torture_ops trivial_ops = {
        .name           = "trivial"
 };
 
+/*
+ * Definitions for rude RCU-tasks torture testing.
+ */
+
+static void rcu_tasks_rude_torture_deferred_free(struct rcu_torture *p)
+{
+       call_rcu_tasks_rude(&p->rtort_rcu, rcu_torture_cb);
+}
+
+static struct rcu_torture_ops tasks_rude_ops = {
+       .ttype          = RCU_TASKS_RUDE_FLAVOR,
+       .init           = rcu_sync_torture_init,
+       .readlock       = rcu_torture_read_lock_trivial,
+       .read_delay     = rcu_read_delay,  /* just reuse rcu's version. */
+       .readunlock     = rcu_torture_read_unlock_trivial,
+       .get_gp_seq     = rcu_no_completed,
+       .deferred_free  = rcu_tasks_rude_torture_deferred_free,
+       .sync           = synchronize_rcu_tasks_rude,
+       .exp_sync       = synchronize_rcu_tasks_rude,
+       .call           = call_rcu_tasks_rude,
+       .cb_barrier     = rcu_barrier_tasks_rude,
+       .fqs            = NULL,
+       .stats          = NULL,
+       .irq_capable    = 1,
+       .name           = "tasks-rude"
+};
+
+/*
+ * Definitions for tracing RCU-tasks torture testing.
+ */
+
+static int tasks_tracing_torture_read_lock(void)
+{
+       rcu_read_lock_trace();
+       return 0;
+}
+
+static void tasks_tracing_torture_read_unlock(int idx)
+{
+       rcu_read_unlock_trace();
+}
+
+static void rcu_tasks_tracing_torture_deferred_free(struct rcu_torture *p)
+{
+       call_rcu_tasks_trace(&p->rtort_rcu, rcu_torture_cb);
+}
+
+static struct rcu_torture_ops tasks_tracing_ops = {
+       .ttype          = RCU_TASKS_TRACING_FLAVOR,
+       .init           = rcu_sync_torture_init,
+       .readlock       = tasks_tracing_torture_read_lock,
+       .read_delay     = srcu_read_delay,  /* just reuse srcu's version. */
+       .readunlock     = tasks_tracing_torture_read_unlock,
+       .get_gp_seq     = rcu_no_completed,
+       .deferred_free  = rcu_tasks_tracing_torture_deferred_free,
+       .sync           = synchronize_rcu_tasks_trace,
+       .exp_sync       = synchronize_rcu_tasks_trace,
+       .call           = call_rcu_tasks_trace,
+       .cb_barrier     = rcu_barrier_tasks_trace,
+       .fqs            = NULL,
+       .stats          = NULL,
+       .irq_capable    = 1,
+       .slow_gps       = 1,
+       .name           = "tasks-tracing"
+};
+
 static unsigned long rcutorture_seq_diff(unsigned long new, unsigned long old)
 {
        if (!cur_ops->gp_diff)
@@ -734,7 +821,7 @@ static unsigned long rcutorture_seq_diff(unsigned long new, unsigned long old)
 
 static bool __maybe_unused torturing_tasks(void)
 {
-       return cur_ops == &tasks_ops;
+       return cur_ops == &tasks_ops || cur_ops == &tasks_rude_ops;
 }
 
 /*
@@ -833,7 +920,7 @@ static int rcu_torture_boost(void *arg)
 
                /* Wait for the next test interval. */
                oldstarttime = boost_starttime;
-               while (ULONG_CMP_LT(jiffies, oldstarttime)) {
+               while (time_before(jiffies, oldstarttime)) {
                        schedule_timeout_interruptible(oldstarttime - jiffies);
                        stutter_wait("rcu_torture_boost");
                        if (torture_must_stop())
@@ -843,7 +930,7 @@ static int rcu_torture_boost(void *arg)
                /* Do one boost-test interval. */
                endtime = oldstarttime + test_boost_duration * HZ;
                call_rcu_time = jiffies;
-               while (ULONG_CMP_LT(jiffies, endtime)) {
+               while (time_before(jiffies, endtime)) {
                        /* If we don't have a callback in flight, post one. */
                        if (!smp_load_acquire(&rbi.inflight)) {
                                /* RCU core before ->inflight = 1. */
@@ -914,7 +1001,7 @@ rcu_torture_fqs(void *arg)
        VERBOSE_TOROUT_STRING("rcu_torture_fqs task started");
        do {
                fqs_resume_time = jiffies + fqs_stutter * HZ;
-               while (ULONG_CMP_LT(jiffies, fqs_resume_time) &&
+               while (time_before(jiffies, fqs_resume_time) &&
                       !kthread_should_stop()) {
                        schedule_timeout_interruptible(1);
                }
@@ -1147,6 +1234,7 @@ static void rcutorture_one_extend(int *readstate, int newstate,
                                  struct torture_random_state *trsp,
                                  struct rt_read_seg *rtrsp)
 {
+       unsigned long flags;
        int idxnew = -1;
        int idxold = *readstate;
        int statesnew = ~*readstate & newstate;
@@ -1181,8 +1269,15 @@ static void rcutorture_one_extend(int *readstate, int newstate,
                rcu_read_unlock_bh();
        if (statesold & RCUTORTURE_RDR_SCHED)
                rcu_read_unlock_sched();
-       if (statesold & RCUTORTURE_RDR_RCU)
+       if (statesold & RCUTORTURE_RDR_RCU) {
+               bool lockit = !statesnew && !(torture_random(trsp) & 0xffff);
+
+               if (lockit)
+                       raw_spin_lock_irqsave(&current->pi_lock, flags);
                cur_ops->readunlock(idxold >> RCUTORTURE_RDR_SHIFT);
+               if (lockit)
+                       raw_spin_unlock_irqrestore(&current->pi_lock, flags);
+       }
 
        /* Delay if neither beginning nor end and there was a change. */
        if ((statesnew || statesold) && *readstate && newstate)
@@ -1283,6 +1378,7 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp)
                                  rcu_read_lock_bh_held() ||
                                  rcu_read_lock_sched_held() ||
                                  srcu_read_lock_held(srcu_ctlp) ||
+                                 rcu_read_lock_trace_held() ||
                                  torturing_tasks());
        if (p == NULL) {
                /* Wait for rcu_torture_writer to get underway */
@@ -1444,9 +1540,9 @@ rcu_torture_stats_print(void)
                atomic_long_read(&n_rcu_torture_timers));
        torture_onoff_stats();
        pr_cont("barrier: %ld/%ld:%ld\n",
-               n_barrier_successes,
-               n_barrier_attempts,
-               n_rcu_torture_barrier_error);
+               data_race(n_barrier_successes),
+               data_race(n_barrier_attempts),
+               data_race(n_rcu_torture_barrier_error));
 
        pr_alert("%s%s ", torture_type, TORTURE_FLAG);
        if (atomic_read(&n_rcu_torture_mberror) ||
@@ -1536,6 +1632,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
                 "test_boost=%d/%d test_boost_interval=%d "
                 "test_boost_duration=%d shutdown_secs=%d "
                 "stall_cpu=%d stall_cpu_holdoff=%d stall_cpu_irqsoff=%d "
+                "stall_cpu_block=%d "
                 "n_barrier_cbs=%d "
                 "onoff_interval=%d onoff_holdoff=%d\n",
                 torture_type, tag, nrealreaders, nfakewriters,
@@ -1544,6 +1641,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
                 test_boost, cur_ops->can_boost,
                 test_boost_interval, test_boost_duration, shutdown_secs,
                 stall_cpu, stall_cpu_holdoff, stall_cpu_irqsoff,
+                stall_cpu_block,
                 n_barrier_cbs,
                 onoff_interval, onoff_holdoff);
 }
@@ -1599,6 +1697,7 @@ static int rcutorture_booster_init(unsigned int cpu)
  */
 static int rcu_torture_stall(void *args)
 {
+       int idx;
        unsigned long stop_at;
 
        VERBOSE_TOROUT_STRING("rcu_torture_stall task started");
@@ -1607,26 +1706,37 @@ static int rcu_torture_stall(void *args)
                schedule_timeout_interruptible(stall_cpu_holdoff * HZ);
                VERBOSE_TOROUT_STRING("rcu_torture_stall end holdoff");
        }
-       if (!kthread_should_stop()) {
+       if (!kthread_should_stop() && stall_gp_kthread > 0) {
+               VERBOSE_TOROUT_STRING("rcu_torture_stall begin GP stall");
+               rcu_gp_set_torture_wait(stall_gp_kthread * HZ);
+               for (idx = 0; idx < stall_gp_kthread + 2; idx++) {
+                       if (kthread_should_stop())
+                               break;
+                       schedule_timeout_uninterruptible(HZ);
+               }
+       }
+       if (!kthread_should_stop() && stall_cpu > 0) {
+               VERBOSE_TOROUT_STRING("rcu_torture_stall begin CPU stall");
                stop_at = ktime_get_seconds() + stall_cpu;
                /* RCU CPU stall is expected behavior in following code. */
-               rcu_read_lock();
+               idx = cur_ops->readlock();
                if (stall_cpu_irqsoff)
                        local_irq_disable();
-               else
+               else if (!stall_cpu_block)
                        preempt_disable();
                pr_alert("rcu_torture_stall start on CPU %d.\n",
-                        smp_processor_id());
+                        raw_smp_processor_id());
                while (ULONG_CMP_LT((unsigned long)ktime_get_seconds(),
                                    stop_at))
-                       continue;  /* Induce RCU CPU stall warning. */
+                       if (stall_cpu_block)
+                               schedule_timeout_uninterruptible(HZ);
                if (stall_cpu_irqsoff)
                        local_irq_enable();
-               else
+               else if (!stall_cpu_block)
                        preempt_enable();
-               rcu_read_unlock();
-               pr_alert("rcu_torture_stall end.\n");
+               cur_ops->readunlock(idx);
        }
+       pr_alert("rcu_torture_stall end.\n");
        torture_shutdown_absorb("rcu_torture_stall");
        while (!kthread_should_stop())
                schedule_timeout_interruptible(10 * HZ);
@@ -1636,7 +1746,7 @@ static int rcu_torture_stall(void *args)
 /* Spawn CPU-stall kthread, if stall_cpu specified. */
 static int __init rcu_torture_stall_init(void)
 {
-       if (stall_cpu <= 0)
+       if (stall_cpu <= 0 && stall_gp_kthread <= 0)
                return 0;
        return torture_create_kthread(rcu_torture_stall, NULL, stall_task);
 }
@@ -1692,8 +1802,8 @@ struct rcu_fwd {
        unsigned long rcu_launder_gp_seq_start;
 };
 
-struct rcu_fwd *rcu_fwds;
-bool rcu_fwd_emergency_stop;
+static struct rcu_fwd *rcu_fwds;
+static bool rcu_fwd_emergency_stop;
 
 static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp)
 {
@@ -2400,7 +2510,8 @@ rcu_torture_init(void)
        int firsterr = 0;
        static struct rcu_torture_ops *torture_ops[] = {
                &rcu_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
-               &busted_srcud_ops, &tasks_ops, &trivial_ops,
+               &busted_srcud_ops, &tasks_ops, &tasks_rude_ops,
+               &tasks_tracing_ops, &trivial_ops,
        };
 
        if (!torture_init_begin(torture_type, verbose))
index 0c71505f0e19cab229ccefea8f8089e17881dfba..6d3ef700fb0e174e9522c2efdd4108d28f846bdd 100644 (file)
 #include "rcu.h"
 #include "rcu_segcblist.h"
 
+#ifndef data_race
+#define data_race(expr)                                                        \
+       ({                                                              \
+               expr;                                                   \
+       })
+#endif
+#ifndef ASSERT_EXCLUSIVE_WRITER
+#define ASSERT_EXCLUSIVE_WRITER(var) do { } while (0)
+#endif
+#ifndef ASSERT_EXCLUSIVE_ACCESS
+#define ASSERT_EXCLUSIVE_ACCESS(var) do { } while (0)
+#endif
+
 /* Holdoff in nanoseconds for auto-expediting. */
 #define DEFAULT_SRCU_EXP_HOLDOFF (25 * 1000)
 static ulong exp_holdoff = DEFAULT_SRCU_EXP_HOLDOFF;
@@ -1268,8 +1281,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
                struct srcu_data *sdp;
 
                sdp = per_cpu_ptr(ssp->sda, cpu);
-               u0 = sdp->srcu_unlock_count[!idx];
-               u1 = sdp->srcu_unlock_count[idx];
+               u0 = data_race(sdp->srcu_unlock_count[!idx]);
+               u1 = data_race(sdp->srcu_unlock_count[idx]);
 
                /*
                 * Make sure that a lock is always counted if the corresponding
@@ -1277,8 +1290,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
                 */
                smp_rmb();
 
-               l0 = sdp->srcu_lock_count[!idx];
-               l1 = sdp->srcu_lock_count[idx];
+               l0 = data_race(sdp->srcu_lock_count[!idx]);
+               l1 = data_race(sdp->srcu_lock_count[idx]);
 
                c0 = l0 - u0;
                c1 = l1 - u1;
diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
new file mode 100644 (file)
index 0000000..ce23f6c
--- /dev/null
@@ -0,0 +1,1193 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Task-based RCU implementations.
+ *
+ * Copyright (C) 2020 Paul E. McKenney
+ */
+
+#ifdef CONFIG_TASKS_RCU_GENERIC
+
+////////////////////////////////////////////////////////////////////////
+//
+// Generic data structures.
+
+struct rcu_tasks;
+typedef void (*rcu_tasks_gp_func_t)(struct rcu_tasks *rtp);
+typedef void (*pregp_func_t)(void);
+typedef void (*pertask_func_t)(struct task_struct *t, struct list_head *hop);
+typedef void (*postscan_func_t)(struct list_head *hop);
+typedef void (*holdouts_func_t)(struct list_head *hop, bool ndrpt, bool *frptp);
+typedef void (*postgp_func_t)(struct rcu_tasks *rtp);
+
+/**
+ * Definition for a Tasks-RCU-like mechanism.
+ * @cbs_head: Head of callback list.
+ * @cbs_tail: Tail pointer for callback list.
+ * @cbs_wq: Wait queue allowning new callback to get kthread's attention.
+ * @cbs_lock: Lock protecting callback list.
+ * @kthread_ptr: This flavor's grace-period/callback-invocation kthread.
+ * @gp_func: This flavor's grace-period-wait function.
+ * @gp_state: Grace period's most recent state transition (debugging).
+ * @gp_jiffies: Time of last @gp_state transition.
+ * @gp_start: Most recent grace-period start in jiffies.
+ * @n_gps: Number of grace periods completed since boot.
+ * @n_ipis: Number of IPIs sent to encourage grace periods to end.
+ * @n_ipis_fails: Number of IPI-send failures.
+ * @pregp_func: This flavor's pre-grace-period function (optional).
+ * @pertask_func: This flavor's per-task scan function (optional).
+ * @postscan_func: This flavor's post-task scan function (optional).
+ * @holdout_func: This flavor's holdout-list scan function (optional).
+ * @postgp_func: This flavor's post-grace-period function (optional).
+ * @call_func: This flavor's call_rcu()-equivalent function.
+ * @name: This flavor's textual name.
+ * @kname: This flavor's kthread name.
+ */
+struct rcu_tasks {
+       struct rcu_head *cbs_head;
+       struct rcu_head **cbs_tail;
+       struct wait_queue_head cbs_wq;
+       raw_spinlock_t cbs_lock;
+       int gp_state;
+       unsigned long gp_jiffies;
+       unsigned long gp_start;
+       unsigned long n_gps;
+       unsigned long n_ipis;
+       unsigned long n_ipis_fails;
+       struct task_struct *kthread_ptr;
+       rcu_tasks_gp_func_t gp_func;
+       pregp_func_t pregp_func;
+       pertask_func_t pertask_func;
+       postscan_func_t postscan_func;
+       holdouts_func_t holdouts_func;
+       postgp_func_t postgp_func;
+       call_rcu_func_t call_func;
+       char *name;
+       char *kname;
+};
+
+#define DEFINE_RCU_TASKS(rt_name, gp, call, n)                         \
+static struct rcu_tasks rt_name =                                      \
+{                                                                      \
+       .cbs_tail = &rt_name.cbs_head,                                  \
+       .cbs_wq = __WAIT_QUEUE_HEAD_INITIALIZER(rt_name.cbs_wq),        \
+       .cbs_lock = __RAW_SPIN_LOCK_UNLOCKED(rt_name.cbs_lock),         \
+       .gp_func = gp,                                                  \
+       .call_func = call,                                              \
+       .name = n,                                                      \
+       .kname = #rt_name,                                              \
+}
+
+/* Track exiting tasks in order to allow them to be waited for. */
+DEFINE_STATIC_SRCU(tasks_rcu_exit_srcu);
+
+/* Avoid IPIing CPUs early in the grace period. */
+#define RCU_TASK_IPI_DELAY (HZ / 2)
+static int rcu_task_ipi_delay __read_mostly = RCU_TASK_IPI_DELAY;
+module_param(rcu_task_ipi_delay, int, 0644);
+
+/* Control stall timeouts.  Disable with <= 0, otherwise jiffies till stall. */
+#define RCU_TASK_STALL_TIMEOUT (HZ * 60 * 10)
+static int rcu_task_stall_timeout __read_mostly = RCU_TASK_STALL_TIMEOUT;
+module_param(rcu_task_stall_timeout, int, 0644);
+
+/* RCU tasks grace-period state for debugging. */
+#define RTGS_INIT               0
+#define RTGS_WAIT_WAIT_CBS      1
+#define RTGS_WAIT_GP            2
+#define RTGS_PRE_WAIT_GP        3
+#define RTGS_SCAN_TASKLIST      4
+#define RTGS_POST_SCAN_TASKLIST         5
+#define RTGS_WAIT_SCAN_HOLDOUTS         6
+#define RTGS_SCAN_HOLDOUTS      7
+#define RTGS_POST_GP            8
+#define RTGS_WAIT_READERS       9
+#define RTGS_INVOKE_CBS                10
+#define RTGS_WAIT_CBS          11
+static const char * const rcu_tasks_gp_state_names[] = {
+       "RTGS_INIT",
+       "RTGS_WAIT_WAIT_CBS",
+       "RTGS_WAIT_GP",
+       "RTGS_PRE_WAIT_GP",
+       "RTGS_SCAN_TASKLIST",
+       "RTGS_POST_SCAN_TASKLIST",
+       "RTGS_WAIT_SCAN_HOLDOUTS",
+       "RTGS_SCAN_HOLDOUTS",
+       "RTGS_POST_GP",
+       "RTGS_WAIT_READERS",
+       "RTGS_INVOKE_CBS",
+       "RTGS_WAIT_CBS",
+};
+
+////////////////////////////////////////////////////////////////////////
+//
+// Generic code.
+
+/* Record grace-period phase and time. */
+static void set_tasks_gp_state(struct rcu_tasks *rtp, int newstate)
+{
+       rtp->gp_state = newstate;
+       rtp->gp_jiffies = jiffies;
+}
+
+/* Return state name. */
+static const char *tasks_gp_state_getname(struct rcu_tasks *rtp)
+{
+       int i = data_race(rtp->gp_state); // Let KCSAN detect update races
+       int j = READ_ONCE(i); // Prevent the compiler from reading twice
+
+       if (j >= ARRAY_SIZE(rcu_tasks_gp_state_names))
+               return "???";
+       return rcu_tasks_gp_state_names[j];
+}
+
+// Enqueue a callback for the specified flavor of Tasks RCU.
+static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func,
+                                  struct rcu_tasks *rtp)
+{
+       unsigned long flags;
+       bool needwake;
+
+       rhp->next = NULL;
+       rhp->func = func;
+       raw_spin_lock_irqsave(&rtp->cbs_lock, flags);
+       needwake = !rtp->cbs_head;
+       WRITE_ONCE(*rtp->cbs_tail, rhp);
+       rtp->cbs_tail = &rhp->next;
+       raw_spin_unlock_irqrestore(&rtp->cbs_lock, flags);
+       /* We can't create the thread unless interrupts are enabled. */
+       if (needwake && READ_ONCE(rtp->kthread_ptr))
+               wake_up(&rtp->cbs_wq);
+}
+
+// Wait for a grace period for the specified flavor of Tasks RCU.
+static void synchronize_rcu_tasks_generic(struct rcu_tasks *rtp)
+{
+       /* Complain if the scheduler has not started.  */
+       RCU_LOCKDEP_WARN(rcu_scheduler_active == RCU_SCHEDULER_INACTIVE,
+                        "synchronize_rcu_tasks called too soon");
+
+       /* Wait for the grace period. */
+       wait_rcu_gp(rtp->call_func);
+}
+
+/* RCU-tasks kthread that detects grace periods and invokes callbacks. */
+static int __noreturn rcu_tasks_kthread(void *arg)
+{
+       unsigned long flags;
+       struct rcu_head *list;
+       struct rcu_head *next;
+       struct rcu_tasks *rtp = arg;
+
+       /* Run on housekeeping CPUs by default.  Sysadm can move if desired. */
+       housekeeping_affine(current, HK_FLAG_RCU);
+       WRITE_ONCE(rtp->kthread_ptr, current); // Let GPs start!
+
+       /*
+        * Each pass through the following loop makes one check for
+        * newly arrived callbacks, and, if there are some, waits for
+        * one RCU-tasks grace period and then invokes the callbacks.
+        * This loop is terminated by the system going down.  ;-)
+        */
+       for (;;) {
+
+               /* Pick up any new callbacks. */
+               raw_spin_lock_irqsave(&rtp->cbs_lock, flags);
+               smp_mb__after_spinlock(); // Order updates vs. GP.
+               list = rtp->cbs_head;
+               rtp->cbs_head = NULL;
+               rtp->cbs_tail = &rtp->cbs_head;
+               raw_spin_unlock_irqrestore(&rtp->cbs_lock, flags);
+
+               /* If there were none, wait a bit and start over. */
+               if (!list) {
+                       wait_event_interruptible(rtp->cbs_wq,
+                                                READ_ONCE(rtp->cbs_head));
+                       if (!rtp->cbs_head) {
+                               WARN_ON(signal_pending(current));
+                               set_tasks_gp_state(rtp, RTGS_WAIT_WAIT_CBS);
+                               schedule_timeout_interruptible(HZ/10);
+                       }
+                       continue;
+               }
+
+               // Wait for one grace period.
+               set_tasks_gp_state(rtp, RTGS_WAIT_GP);
+               rtp->gp_start = jiffies;
+               rtp->gp_func(rtp);
+               rtp->n_gps++;
+
+               /* Invoke the callbacks. */
+               set_tasks_gp_state(rtp, RTGS_INVOKE_CBS);
+               while (list) {
+                       next = list->next;
+                       local_bh_disable();
+                       list->func(list);
+                       local_bh_enable();
+                       list = next;
+                       cond_resched();
+               }
+               /* Paranoid sleep to keep this from entering a tight loop */
+               schedule_timeout_uninterruptible(HZ/10);
+
+               set_tasks_gp_state(rtp, RTGS_WAIT_CBS);
+       }
+}
+
+/* Spawn RCU-tasks grace-period kthread, e.g., at core_initcall() time. */
+static void __init rcu_spawn_tasks_kthread_generic(struct rcu_tasks *rtp)
+{
+       struct task_struct *t;
+
+       t = kthread_run(rcu_tasks_kthread, rtp, "%s_kthread", rtp->kname);
+       if (WARN_ONCE(IS_ERR(t), "%s: Could not start %s grace-period kthread, OOM is now expected behavior\n", __func__, rtp->name))
+               return;
+       smp_mb(); /* Ensure others see full kthread. */
+}
+
+#ifndef CONFIG_TINY_RCU
+
+/*
+ * Print any non-default Tasks RCU settings.
+ */
+static void __init rcu_tasks_bootup_oddness(void)
+{
+#if defined(CONFIG_TASKS_RCU) || defined(CONFIG_TASKS_TRACE_RCU)
+       if (rcu_task_stall_timeout != RCU_TASK_STALL_TIMEOUT)
+               pr_info("\tTasks-RCU CPU stall warnings timeout set to %d (rcu_task_stall_timeout).\n", rcu_task_stall_timeout);
+#endif /* #ifdef CONFIG_TASKS_RCU */
+#ifdef CONFIG_TASKS_RCU
+       pr_info("\tTrampoline variant of Tasks RCU enabled.\n");
+#endif /* #ifdef CONFIG_TASKS_RCU */
+#ifdef CONFIG_TASKS_RUDE_RCU
+       pr_info("\tRude variant of Tasks RCU enabled.\n");
+#endif /* #ifdef CONFIG_TASKS_RUDE_RCU */
+#ifdef CONFIG_TASKS_TRACE_RCU
+       pr_info("\tTracing variant of Tasks RCU enabled.\n");
+#endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
+}
+
+#endif /* #ifndef CONFIG_TINY_RCU */
+
+/* Dump out rcutorture-relevant state common to all RCU-tasks flavors. */
+static void show_rcu_tasks_generic_gp_kthread(struct rcu_tasks *rtp, char *s)
+{
+       pr_info("%s: %s(%d) since %lu g:%lu i:%lu/%lu %c%c %s\n",
+               rtp->kname,
+               tasks_gp_state_getname(rtp), data_race(rtp->gp_state),
+               jiffies - data_race(rtp->gp_jiffies),
+               data_race(rtp->n_gps),
+               data_race(rtp->n_ipis_fails), data_race(rtp->n_ipis),
+               ".k"[!!data_race(rtp->kthread_ptr)],
+               ".C"[!!data_race(rtp->cbs_head)],
+               s);
+}
+
+static void exit_tasks_rcu_finish_trace(struct task_struct *t);
+
+#if defined(CONFIG_TASKS_RCU) || defined(CONFIG_TASKS_TRACE_RCU)
+
+////////////////////////////////////////////////////////////////////////
+//
+// Shared code between task-list-scanning variants of Tasks RCU.
+
+/* Wait for one RCU-tasks grace period. */
+static void rcu_tasks_wait_gp(struct rcu_tasks *rtp)
+{
+       struct task_struct *g, *t;
+       unsigned long lastreport;
+       LIST_HEAD(holdouts);
+       int fract;
+
+       set_tasks_gp_state(rtp, RTGS_PRE_WAIT_GP);
+       rtp->pregp_func();
+
+       /*
+        * There were callbacks, so we need to wait for an RCU-tasks
+        * grace period.  Start off by scanning the task list for tasks
+        * that are not already voluntarily blocked.  Mark these tasks
+        * and make a list of them in holdouts.
+        */
+       set_tasks_gp_state(rtp, RTGS_SCAN_TASKLIST);
+       rcu_read_lock();
+       for_each_process_thread(g, t)
+               rtp->pertask_func(t, &holdouts);
+       rcu_read_unlock();
+
+       set_tasks_gp_state(rtp, RTGS_POST_SCAN_TASKLIST);
+       rtp->postscan_func(&holdouts);
+
+       /*
+        * Each pass through the following loop scans the list of holdout
+        * tasks, removing any that are no longer holdouts.  When the list
+        * is empty, we are done.
+        */
+       lastreport = jiffies;
+
+       /* Start off with HZ/10 wait and slowly back off to 1 HZ wait. */
+       fract = 10;
+
+       for (;;) {
+               bool firstreport;
+               bool needreport;
+               int rtst;
+
+               if (list_empty(&holdouts))
+                       break;
+
+               /* Slowly back off waiting for holdouts */
+               set_tasks_gp_state(rtp, RTGS_WAIT_SCAN_HOLDOUTS);
+               schedule_timeout_interruptible(HZ/fract);
+
+               if (fract > 1)
+                       fract--;
+
+               rtst = READ_ONCE(rcu_task_stall_timeout);
+               needreport = rtst > 0 && time_after(jiffies, lastreport + rtst);
+               if (needreport)
+                       lastreport = jiffies;
+               firstreport = true;
+               WARN_ON(signal_pending(current));
+               set_tasks_gp_state(rtp, RTGS_SCAN_HOLDOUTS);
+               rtp->holdouts_func(&holdouts, needreport, &firstreport);
+       }
+
+       set_tasks_gp_state(rtp, RTGS_POST_GP);
+       rtp->postgp_func(rtp);
+}
+
+#endif /* #if defined(CONFIG_TASKS_RCU) || defined(CONFIG_TASKS_TRACE_RCU) */
+
+#ifdef CONFIG_TASKS_RCU
+
+////////////////////////////////////////////////////////////////////////
+//
+// Simple variant of RCU whose quiescent states are voluntary context
+// switch, cond_resched_rcu_qs(), user-space execution, and idle.
+// As such, grace periods can take one good long time.  There are no
+// read-side primitives similar to rcu_read_lock() and rcu_read_unlock()
+// because this implementation is intended to get the system into a safe
+// state for some of the manipulations involved in tracing and the like.
+// Finally, this implementation does not support high call_rcu_tasks()
+// rates from multiple CPUs.  If this is required, per-CPU callback lists
+// will be needed.
+
+/* Pre-grace-period preparation. */
+static void rcu_tasks_pregp_step(void)
+{
+       /*
+        * Wait for all pre-existing t->on_rq and t->nvcsw transitions
+        * to complete.  Invoking synchronize_rcu() suffices because all
+        * these transitions occur with interrupts disabled.  Without this
+        * synchronize_rcu(), a read-side critical section that started
+        * before the grace period might be incorrectly seen as having
+        * started after the grace period.
+        *
+        * This synchronize_rcu() also dispenses with the need for a
+        * memory barrier on the first store to t->rcu_tasks_holdout,
+        * as it forces the store to happen after the beginning of the
+        * grace period.
+        */
+       synchronize_rcu();
+}
+
+/* Per-task initial processing. */
+static void rcu_tasks_pertask(struct task_struct *t, struct list_head *hop)
+{
+       if (t != current && READ_ONCE(t->on_rq) && !is_idle_task(t)) {
+               get_task_struct(t);
+               t->rcu_tasks_nvcsw = READ_ONCE(t->nvcsw);
+               WRITE_ONCE(t->rcu_tasks_holdout, true);
+               list_add(&t->rcu_tasks_holdout_list, hop);
+       }
+}
+
+/* Processing between scanning taskslist and draining the holdout list. */
+void rcu_tasks_postscan(struct list_head *hop)
+{
+       /*
+        * Wait for tasks that are in the process of exiting.  This
+        * does only part of the job, ensuring that all tasks that were
+        * previously exiting reach the point where they have disabled
+        * preemption, allowing the later synchronize_rcu() to finish
+        * the job.
+        */
+       synchronize_srcu(&tasks_rcu_exit_srcu);
+}
+
+/* See if tasks are still holding out, complain if so. */
+static void check_holdout_task(struct task_struct *t,
+                              bool needreport, bool *firstreport)
+{
+       int cpu;
+
+       if (!READ_ONCE(t->rcu_tasks_holdout) ||
+           t->rcu_tasks_nvcsw != READ_ONCE(t->nvcsw) ||
+           !READ_ONCE(t->on_rq) ||
+           (IS_ENABLED(CONFIG_NO_HZ_FULL) &&
+            !is_idle_task(t) && t->rcu_tasks_idle_cpu >= 0)) {
+               WRITE_ONCE(t->rcu_tasks_holdout, false);
+               list_del_init(&t->rcu_tasks_holdout_list);
+               put_task_struct(t);
+               return;
+       }
+       rcu_request_urgent_qs_task(t);
+       if (!needreport)
+               return;
+       if (*firstreport) {
+               pr_err("INFO: rcu_tasks detected stalls on tasks:\n");
+               *firstreport = false;
+       }
+       cpu = task_cpu(t);
+       pr_alert("%p: %c%c nvcsw: %lu/%lu holdout: %d idle_cpu: %d/%d\n",
+                t, ".I"[is_idle_task(t)],
+                "N."[cpu < 0 || !tick_nohz_full_cpu(cpu)],
+                t->rcu_tasks_nvcsw, t->nvcsw, t->rcu_tasks_holdout,
+                t->rcu_tasks_idle_cpu, cpu);
+       sched_show_task(t);
+}
+
+/* Scan the holdout lists for tasks no longer holding out. */
+static void check_all_holdout_tasks(struct list_head *hop,
+                                   bool needreport, bool *firstreport)
+{
+       struct task_struct *t, *t1;
+
+       list_for_each_entry_safe(t, t1, hop, rcu_tasks_holdout_list) {
+               check_holdout_task(t, needreport, firstreport);
+               cond_resched();
+       }
+}
+
+/* Finish off the Tasks-RCU grace period. */
+static void rcu_tasks_postgp(struct rcu_tasks *rtp)
+{
+       /*
+        * Because ->on_rq and ->nvcsw are not guaranteed to have a full
+        * memory barriers prior to them in the schedule() path, memory
+        * reordering on other CPUs could cause their RCU-tasks read-side
+        * critical sections to extend past the end of the grace period.
+        * However, because these ->nvcsw updates are carried out with
+        * interrupts disabled, we can use synchronize_rcu() to force the
+        * needed ordering on all such CPUs.
+        *
+        * This synchronize_rcu() also confines all ->rcu_tasks_holdout
+        * accesses to be within the grace period, avoiding the need for
+        * memory barriers for ->rcu_tasks_holdout accesses.
+        *
+        * In addition, this synchronize_rcu() waits for exiting tasks
+        * to complete their final preempt_disable() region of execution,
+        * cleaning up after the synchronize_srcu() above.
+        */
+       synchronize_rcu();
+}
+
+void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func);
+DEFINE_RCU_TASKS(rcu_tasks, rcu_tasks_wait_gp, call_rcu_tasks, "RCU Tasks");
+
+/**
+ * call_rcu_tasks() - Queue an RCU for invocation task-based grace period
+ * @rhp: structure to be used for queueing the RCU updates.
+ * @func: actual callback function to be invoked after the grace period
+ *
+ * The callback function will be invoked some time after a full grace
+ * period elapses, in other words after all currently executing RCU
+ * read-side critical sections have completed. call_rcu_tasks() assumes
+ * that the read-side critical sections end at a voluntary context
+ * switch (not a preemption!), cond_resched_rcu_qs(), entry into idle,
+ * or transition to usermode execution.  As such, there are no read-side
+ * primitives analogous to rcu_read_lock() and rcu_read_unlock() because
+ * this primitive is intended to determine that all tasks have passed
+ * through a safe state, not so much for data-strcuture synchronization.
+ *
+ * See the description of call_rcu() for more detailed information on
+ * memory ordering guarantees.
+ */
+void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)
+{
+       call_rcu_tasks_generic(rhp, func, &rcu_tasks);
+}
+EXPORT_SYMBOL_GPL(call_rcu_tasks);
+
+/**
+ * synchronize_rcu_tasks - wait until an rcu-tasks grace period has elapsed.
+ *
+ * Control will return to the caller some time after a full rcu-tasks
+ * grace period has elapsed, in other words after all currently
+ * executing rcu-tasks read-side critical sections have elapsed.  These
+ * read-side critical sections are delimited by calls to schedule(),
+ * cond_resched_tasks_rcu_qs(), idle execution, userspace execution, calls
+ * to synchronize_rcu_tasks(), and (in theory, anyway) cond_resched().
+ *
+ * This is a very specialized primitive, intended only for a few uses in
+ * tracing and other situations requiring manipulation of function
+ * preambles and profiling hooks.  The synchronize_rcu_tasks() function
+ * is not (yet) intended for heavy use from multiple CPUs.
+ *
+ * See the description of synchronize_rcu() for more detailed information
+ * on memory ordering guarantees.
+ */
+void synchronize_rcu_tasks(void)
+{
+       synchronize_rcu_tasks_generic(&rcu_tasks);
+}
+EXPORT_SYMBOL_GPL(synchronize_rcu_tasks);
+
+/**
+ * rcu_barrier_tasks - Wait for in-flight call_rcu_tasks() callbacks.
+ *
+ * Although the current implementation is guaranteed to wait, it is not
+ * obligated to, for example, if there are no pending callbacks.
+ */
+void rcu_barrier_tasks(void)
+{
+       /* There is only one callback queue, so this is easy.  ;-) */
+       synchronize_rcu_tasks();
+}
+EXPORT_SYMBOL_GPL(rcu_barrier_tasks);
+
+static int __init rcu_spawn_tasks_kthread(void)
+{
+       rcu_tasks.pregp_func = rcu_tasks_pregp_step;
+       rcu_tasks.pertask_func = rcu_tasks_pertask;
+       rcu_tasks.postscan_func = rcu_tasks_postscan;
+       rcu_tasks.holdouts_func = check_all_holdout_tasks;
+       rcu_tasks.postgp_func = rcu_tasks_postgp;
+       rcu_spawn_tasks_kthread_generic(&rcu_tasks);
+       return 0;
+}
+core_initcall(rcu_spawn_tasks_kthread);
+
+static void show_rcu_tasks_classic_gp_kthread(void)
+{
+       show_rcu_tasks_generic_gp_kthread(&rcu_tasks, "");
+}
+
+/* Do the srcu_read_lock() for the above synchronize_srcu().  */
+void exit_tasks_rcu_start(void) __acquires(&tasks_rcu_exit_srcu)
+{
+       preempt_disable();
+       current->rcu_tasks_idx = __srcu_read_lock(&tasks_rcu_exit_srcu);
+       preempt_enable();
+}
+
+/* Do the srcu_read_unlock() for the above synchronize_srcu().  */
+void exit_tasks_rcu_finish(void) __releases(&tasks_rcu_exit_srcu)
+{
+       struct task_struct *t = current;
+
+       preempt_disable();
+       __srcu_read_unlock(&tasks_rcu_exit_srcu, t->rcu_tasks_idx);
+       preempt_enable();
+       exit_tasks_rcu_finish_trace(t);
+}
+
+#else /* #ifdef CONFIG_TASKS_RCU */
+static void show_rcu_tasks_classic_gp_kthread(void) { }
+void exit_tasks_rcu_start(void) { }
+void exit_tasks_rcu_finish(void) { exit_tasks_rcu_finish_trace(current); }
+#endif /* #else #ifdef CONFIG_TASKS_RCU */
+
+#ifdef CONFIG_TASKS_RUDE_RCU
+
+////////////////////////////////////////////////////////////////////////
+//
+// "Rude" variant of Tasks RCU, inspired by Steve Rostedt's trick of
+// passing an empty function to schedule_on_each_cpu().  This approach
+// provides an asynchronous call_rcu_tasks_rude() API and batching
+// of concurrent calls to the synchronous synchronize_rcu_rude() API.
+// This sends IPIs far and wide and induces otherwise unnecessary context
+// switches on all online CPUs, whether idle or not.
+
+// Empty function to allow workqueues to force a context switch.
+static void rcu_tasks_be_rude(struct work_struct *work)
+{
+}
+
+// Wait for one rude RCU-tasks grace period.
+static void rcu_tasks_rude_wait_gp(struct rcu_tasks *rtp)
+{
+       rtp->n_ipis += cpumask_weight(cpu_online_mask);
+       schedule_on_each_cpu(rcu_tasks_be_rude);
+}
+
+void call_rcu_tasks_rude(struct rcu_head *rhp, rcu_callback_t func);
+DEFINE_RCU_TASKS(rcu_tasks_rude, rcu_tasks_rude_wait_gp, call_rcu_tasks_rude,
+                "RCU Tasks Rude");
+
+/**
+ * call_rcu_tasks_rude() - Queue a callback rude task-based grace period
+ * @rhp: structure to be used for queueing the RCU updates.
+ * @func: actual callback function to be invoked after the grace period
+ *
+ * The callback function will be invoked some time after a full grace
+ * period elapses, in other words after all currently executing RCU
+ * read-side critical sections have completed. call_rcu_tasks_rude()
+ * assumes that the read-side critical sections end at context switch,
+ * cond_resched_rcu_qs(), or transition to usermode execution.  As such,
+ * there are no read-side primitives analogous to rcu_read_lock() and
+ * rcu_read_unlock() because this primitive is intended to determine
+ * that all tasks have passed through a safe state, not so much for
+ * data-strcuture synchronization.
+ *
+ * See the description of call_rcu() for more detailed information on
+ * memory ordering guarantees.
+ */
+void call_rcu_tasks_rude(struct rcu_head *rhp, rcu_callback_t func)
+{
+       call_rcu_tasks_generic(rhp, func, &rcu_tasks_rude);
+}
+EXPORT_SYMBOL_GPL(call_rcu_tasks_rude);
+
+/**
+ * synchronize_rcu_tasks_rude - wait for a rude rcu-tasks grace period
+ *
+ * Control will return to the caller some time after a rude rcu-tasks
+ * grace period has elapsed, in other words after all currently
+ * executing rcu-tasks read-side critical sections have elapsed.  These
+ * read-side critical sections are delimited by calls to schedule(),
+ * cond_resched_tasks_rcu_qs(), userspace execution, and (in theory,
+ * anyway) cond_resched().
+ *
+ * This is a very specialized primitive, intended only for a few uses in
+ * tracing and other situations requiring manipulation of function preambles
+ * and profiling hooks.  The synchronize_rcu_tasks_rude() function is not
+ * (yet) intended for heavy use from multiple CPUs.
+ *
+ * See the description of synchronize_rcu() for more detailed information
+ * on memory ordering guarantees.
+ */
+void synchronize_rcu_tasks_rude(void)
+{
+       synchronize_rcu_tasks_generic(&rcu_tasks_rude);
+}
+EXPORT_SYMBOL_GPL(synchronize_rcu_tasks_rude);
+
+/**
+ * rcu_barrier_tasks_rude - Wait for in-flight call_rcu_tasks_rude() callbacks.
+ *
+ * Although the current implementation is guaranteed to wait, it is not
+ * obligated to, for example, if there are no pending callbacks.
+ */
+void rcu_barrier_tasks_rude(void)
+{
+       /* There is only one callback queue, so this is easy.  ;-) */
+       synchronize_rcu_tasks_rude();
+}
+EXPORT_SYMBOL_GPL(rcu_barrier_tasks_rude);
+
+static int __init rcu_spawn_tasks_rude_kthread(void)
+{
+       rcu_spawn_tasks_kthread_generic(&rcu_tasks_rude);
+       return 0;
+}
+core_initcall(rcu_spawn_tasks_rude_kthread);
+
+static void show_rcu_tasks_rude_gp_kthread(void)
+{
+       show_rcu_tasks_generic_gp_kthread(&rcu_tasks_rude, "");
+}
+
+#else /* #ifdef CONFIG_TASKS_RUDE_RCU */
+static void show_rcu_tasks_rude_gp_kthread(void) {}
+#endif /* #else #ifdef CONFIG_TASKS_RUDE_RCU */
+
+////////////////////////////////////////////////////////////////////////
+//
+// Tracing variant of Tasks RCU.  This variant is designed to be used
+// to protect tracing hooks, including those of BPF.  This variant
+// therefore:
+//
+// 1.  Has explicit read-side markers to allow finite grace periods
+//     in the face of in-kernel loops for PREEMPT=n builds.
+//
+// 2.  Protects code in the idle loop, exception entry/exit, and
+//     CPU-hotplug code paths, similar to the capabilities of SRCU.
+//
+// 3.  Avoids expensive read-side instruction, having overhead similar
+//     to that of Preemptible RCU.
+//
+// There are of course downsides.  The grace-period code can send IPIs to
+// CPUs, even when those CPUs are in the idle loop or in nohz_full userspace.
+// It is necessary to scan the full tasklist, much as for Tasks RCU.  There
+// is a single callback queue guarded by a single lock, again, much as for
+// Tasks RCU.  If needed, these downsides can be at least partially remedied.
+//
+// Perhaps most important, this variant of RCU does not affect the vanilla
+// flavors, rcu_preempt and rcu_sched.  The fact that RCU Tasks Trace
+// readers can operate from idle, offline, and exception entry/exit in no
+// way allows rcu_preempt and rcu_sched readers to also do so.
+
+// The lockdep state must be outside of #ifdef to be useful.
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key rcu_lock_trace_key;
+struct lockdep_map rcu_trace_lock_map =
+       STATIC_LOCKDEP_MAP_INIT("rcu_read_lock_trace", &rcu_lock_trace_key);
+EXPORT_SYMBOL_GPL(rcu_trace_lock_map);
+#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+
+#ifdef CONFIG_TASKS_TRACE_RCU
+
+atomic_t trc_n_readers_need_end;       // Number of waited-for readers.
+DECLARE_WAIT_QUEUE_HEAD(trc_wait);     // List of holdout tasks.
+
+// Record outstanding IPIs to each CPU.  No point in sending two...
+static DEFINE_PER_CPU(bool, trc_ipi_to_cpu);
+
+// The number of detections of task quiescent state relying on
+// heavyweight readers executing explicit memory barriers.
+unsigned long n_heavy_reader_attempts;
+unsigned long n_heavy_reader_updates;
+unsigned long n_heavy_reader_ofl_updates;
+
+void call_rcu_tasks_trace(struct rcu_head *rhp, rcu_callback_t func);
+DEFINE_RCU_TASKS(rcu_tasks_trace, rcu_tasks_wait_gp, call_rcu_tasks_trace,
+                "RCU Tasks Trace");
+
+/*
+ * This irq_work handler allows rcu_read_unlock_trace() to be invoked
+ * while the scheduler locks are held.
+ */
+static void rcu_read_unlock_iw(struct irq_work *iwp)
+{
+       wake_up(&trc_wait);
+}
+static DEFINE_IRQ_WORK(rcu_tasks_trace_iw, rcu_read_unlock_iw);
+
+/* If we are the last reader, wake up the grace-period kthread. */
+void rcu_read_unlock_trace_special(struct task_struct *t, int nesting)
+{
+       int nq = t->trc_reader_special.b.need_qs;
+
+       if (IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB) &&
+           t->trc_reader_special.b.need_mb)
+               smp_mb(); // Pairs with update-side barriers.
+       // Update .need_qs before ->trc_reader_nesting for irq/NMI handlers.
+       if (nq)
+               WRITE_ONCE(t->trc_reader_special.b.need_qs, false);
+       WRITE_ONCE(t->trc_reader_nesting, nesting);
+       if (nq && atomic_dec_and_test(&trc_n_readers_need_end))
+               irq_work_queue(&rcu_tasks_trace_iw);
+}
+EXPORT_SYMBOL_GPL(rcu_read_unlock_trace_special);
+
+/* Add a task to the holdout list, if it is not already on the list. */
+static void trc_add_holdout(struct task_struct *t, struct list_head *bhp)
+{
+       if (list_empty(&t->trc_holdout_list)) {
+               get_task_struct(t);
+               list_add(&t->trc_holdout_list, bhp);
+       }
+}
+
+/* Remove a task from the holdout list, if it is in fact present. */
+static void trc_del_holdout(struct task_struct *t)
+{
+       if (!list_empty(&t->trc_holdout_list)) {
+               list_del_init(&t->trc_holdout_list);
+               put_task_struct(t);
+       }
+}
+
+/* IPI handler to check task state. */
+static void trc_read_check_handler(void *t_in)
+{
+       struct task_struct *t = current;
+       struct task_struct *texp = t_in;
+
+       // If the task is no longer running on this CPU, leave.
+       if (unlikely(texp != t)) {
+               if (WARN_ON_ONCE(atomic_dec_and_test(&trc_n_readers_need_end)))
+                       wake_up(&trc_wait);
+               goto reset_ipi; // Already on holdout list, so will check later.
+       }
+
+       // If the task is not in a read-side critical section, and
+       // if this is the last reader, awaken the grace-period kthread.
+       if (likely(!t->trc_reader_nesting)) {
+               if (WARN_ON_ONCE(atomic_dec_and_test(&trc_n_readers_need_end)))
+                       wake_up(&trc_wait);
+               // Mark as checked after decrement to avoid false
+               // positives on the above WARN_ON_ONCE().
+               WRITE_ONCE(t->trc_reader_checked, true);
+               goto reset_ipi;
+       }
+       WRITE_ONCE(t->trc_reader_checked, true);
+
+       // Get here if the task is in a read-side critical section.  Set
+       // its state so that it will awaken the grace-period kthread upon
+       // exit from that critical section.
+       WARN_ON_ONCE(t->trc_reader_special.b.need_qs);
+       WRITE_ONCE(t->trc_reader_special.b.need_qs, true);
+
+reset_ipi:
+       // Allow future IPIs to be sent on CPU and for task.
+       // Also order this IPI handler against any later manipulations of
+       // the intended task.
+       smp_store_release(&per_cpu(trc_ipi_to_cpu, smp_processor_id()), false); // ^^^
+       smp_store_release(&texp->trc_ipi_to_cpu, -1); // ^^^
+}
+
+/* Callback function for scheduler to check locked-down task.  */
+static bool trc_inspect_reader(struct task_struct *t, void *arg)
+{
+       int cpu = task_cpu(t);
+       bool in_qs = false;
+       bool ofl = cpu_is_offline(cpu);
+
+       if (task_curr(t)) {
+               WARN_ON_ONCE(ofl & !is_idle_task(t));
+
+               // If no chance of heavyweight readers, do it the hard way.
+               if (!ofl && !IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB))
+                       return false;
+
+               // If heavyweight readers are enabled on the remote task,
+               // we can inspect its state despite its currently running.
+               // However, we cannot safely change its state.
+               n_heavy_reader_attempts++;
+               if (!ofl && // Check for "running" idle tasks on offline CPUs.
+                   !rcu_dynticks_zero_in_eqs(cpu, &t->trc_reader_nesting))
+                       return false; // No quiescent state, do it the hard way.
+               n_heavy_reader_updates++;
+               if (ofl)
+                       n_heavy_reader_ofl_updates++;
+               in_qs = true;
+       } else {
+               in_qs = likely(!t->trc_reader_nesting);
+       }
+
+       // Mark as checked.  Because this is called from the grace-period
+       // kthread, also remove the task from the holdout list.
+       t->trc_reader_checked = true;
+       trc_del_holdout(t);
+
+       if (in_qs)
+               return true;  // Already in quiescent state, done!!!
+
+       // The task is in a read-side critical section, so set up its
+       // state so that it will awaken the grace-period kthread upon exit
+       // from that critical section.
+       atomic_inc(&trc_n_readers_need_end); // One more to wait on.
+       WARN_ON_ONCE(t->trc_reader_special.b.need_qs);
+       WRITE_ONCE(t->trc_reader_special.b.need_qs, true);
+       return true;
+}
+
+/* Attempt to extract the state for the specified task. */
+static void trc_wait_for_one_reader(struct task_struct *t,
+                                   struct list_head *bhp)
+{
+       int cpu;
+
+       // If a previous IPI is still in flight, let it complete.
+       if (smp_load_acquire(&t->trc_ipi_to_cpu) != -1) // Order IPI
+               return;
+
+       // The current task had better be in a quiescent state.
+       if (t == current) {
+               t->trc_reader_checked = true;
+               trc_del_holdout(t);
+               WARN_ON_ONCE(t->trc_reader_nesting);
+               return;
+       }
+
+       // Attempt to nail down the task for inspection.
+       get_task_struct(t);
+       if (try_invoke_on_locked_down_task(t, trc_inspect_reader, NULL)) {
+               put_task_struct(t);
+               return;
+       }
+       put_task_struct(t);
+
+       // If currently running, send an IPI, either way, add to list.
+       trc_add_holdout(t, bhp);
+       if (task_curr(t) && time_after(jiffies, rcu_tasks_trace.gp_start + rcu_task_ipi_delay)) {
+               // The task is currently running, so try IPIing it.
+               cpu = task_cpu(t);
+
+               // If there is already an IPI outstanding, let it happen.
+               if (per_cpu(trc_ipi_to_cpu, cpu) || t->trc_ipi_to_cpu >= 0)
+                       return;
+
+               atomic_inc(&trc_n_readers_need_end);
+               per_cpu(trc_ipi_to_cpu, cpu) = true;
+               t->trc_ipi_to_cpu = cpu;
+               rcu_tasks_trace.n_ipis++;
+               if (smp_call_function_single(cpu,
+                                            trc_read_check_handler, t, 0)) {
+                       // Just in case there is some other reason for
+                       // failure than the target CPU being offline.
+                       rcu_tasks_trace.n_ipis_fails++;
+                       per_cpu(trc_ipi_to_cpu, cpu) = false;
+                       t->trc_ipi_to_cpu = cpu;
+                       if (atomic_dec_and_test(&trc_n_readers_need_end)) {
+                               WARN_ON_ONCE(1);
+                               wake_up(&trc_wait);
+                       }
+               }
+       }
+}
+
+/* Initialize for a new RCU-tasks-trace grace period. */
+static void rcu_tasks_trace_pregp_step(void)
+{
+       int cpu;
+
+       // Allow for fast-acting IPIs.
+       atomic_set(&trc_n_readers_need_end, 1);
+
+       // There shouldn't be any old IPIs, but...
+       for_each_possible_cpu(cpu)
+               WARN_ON_ONCE(per_cpu(trc_ipi_to_cpu, cpu));
+
+       // Disable CPU hotplug across the tasklist scan.
+       // This also waits for all readers in CPU-hotplug code paths.
+       cpus_read_lock();
+}
+
+/* Do first-round processing for the specified task. */
+static void rcu_tasks_trace_pertask(struct task_struct *t,
+                                   struct list_head *hop)
+{
+       WRITE_ONCE(t->trc_reader_special.b.need_qs, false);
+       WRITE_ONCE(t->trc_reader_checked, false);
+       t->trc_ipi_to_cpu = -1;
+       trc_wait_for_one_reader(t, hop);
+}
+
+/*
+ * Do intermediate processing between task and holdout scans and
+ * pick up the idle tasks.
+ */
+static void rcu_tasks_trace_postscan(struct list_head *hop)
+{
+       int cpu;
+
+       for_each_possible_cpu(cpu)
+               rcu_tasks_trace_pertask(idle_task(cpu), hop);
+
+       // Re-enable CPU hotplug now that the tasklist scan has completed.
+       cpus_read_unlock();
+
+       // Wait for late-stage exiting tasks to finish exiting.
+       // These might have passed the call to exit_tasks_rcu_finish().
+       synchronize_rcu();
+       // Any tasks that exit after this point will set ->trc_reader_checked.
+}
+
+/* Show the state of a task stalling the current RCU tasks trace GP. */
+static void show_stalled_task_trace(struct task_struct *t, bool *firstreport)
+{
+       int cpu;
+
+       if (*firstreport) {
+               pr_err("INFO: rcu_tasks_trace detected stalls on tasks:\n");
+               *firstreport = false;
+       }
+       // FIXME: This should attempt to use try_invoke_on_nonrunning_task().
+       cpu = task_cpu(t);
+       pr_alert("P%d: %c%c%c nesting: %d%c cpu: %d\n",
+                t->pid,
+                ".I"[READ_ONCE(t->trc_ipi_to_cpu) > 0],
+                ".i"[is_idle_task(t)],
+                ".N"[cpu > 0 && tick_nohz_full_cpu(cpu)],
+                t->trc_reader_nesting,
+                " N"[!!t->trc_reader_special.b.need_qs],
+                cpu);
+       sched_show_task(t);
+}
+
+/* List stalled IPIs for RCU tasks trace. */
+static void show_stalled_ipi_trace(void)
+{
+       int cpu;
+
+       for_each_possible_cpu(cpu)
+               if (per_cpu(trc_ipi_to_cpu, cpu))
+                       pr_alert("\tIPI outstanding to CPU %d\n", cpu);
+}
+
+/* Do one scan of the holdout list. */
+static void check_all_holdout_tasks_trace(struct list_head *hop,
+                                         bool needreport, bool *firstreport)
+{
+       struct task_struct *g, *t;
+
+       // Disable CPU hotplug across the holdout list scan.
+       cpus_read_lock();
+
+       list_for_each_entry_safe(t, g, hop, trc_holdout_list) {
+               // If safe and needed, try to check the current task.
+               if (READ_ONCE(t->trc_ipi_to_cpu) == -1 &&
+                   !READ_ONCE(t->trc_reader_checked))
+                       trc_wait_for_one_reader(t, hop);
+
+               // If check succeeded, remove this task from the list.
+               if (READ_ONCE(t->trc_reader_checked))
+                       trc_del_holdout(t);
+               else if (needreport)
+                       show_stalled_task_trace(t, firstreport);
+       }
+
+       // Re-enable CPU hotplug now that the holdout list scan has completed.
+       cpus_read_unlock();
+
+       if (needreport) {
+               if (firstreport)
+                       pr_err("INFO: rcu_tasks_trace detected stalls? (Late IPI?)\n");
+               show_stalled_ipi_trace();
+       }
+}
+
+/* Wait for grace period to complete and provide ordering. */
+static void rcu_tasks_trace_postgp(struct rcu_tasks *rtp)
+{
+       bool firstreport;
+       struct task_struct *g, *t;
+       LIST_HEAD(holdouts);
+       long ret;
+
+       // Remove the safety count.
+       smp_mb__before_atomic();  // Order vs. earlier atomics
+       atomic_dec(&trc_n_readers_need_end);
+       smp_mb__after_atomic();  // Order vs. later atomics
+
+       // Wait for readers.
+       set_tasks_gp_state(rtp, RTGS_WAIT_READERS);
+       for (;;) {
+               ret = wait_event_idle_exclusive_timeout(
+                               trc_wait,
+                               atomic_read(&trc_n_readers_need_end) == 0,
+                               READ_ONCE(rcu_task_stall_timeout));
+               if (ret)
+                       break;  // Count reached zero.
+               // Stall warning time, so make a list of the offenders.
+               for_each_process_thread(g, t)
+                       if (READ_ONCE(t->trc_reader_special.b.need_qs))
+                               trc_add_holdout(t, &holdouts);
+               firstreport = true;
+               list_for_each_entry_safe(t, g, &holdouts, trc_holdout_list)
+                       if (READ_ONCE(t->trc_reader_special.b.need_qs)) {
+                               show_stalled_task_trace(t, &firstreport);
+                               trc_del_holdout(t);
+                       }
+               if (firstreport)
+                       pr_err("INFO: rcu_tasks_trace detected stalls? (Counter/taskslist mismatch?)\n");
+               show_stalled_ipi_trace();
+               pr_err("\t%d holdouts\n", atomic_read(&trc_n_readers_need_end));
+       }
+       smp_mb(); // Caller's code must be ordered after wakeup.
+                 // Pairs with pretty much every ordering primitive.
+}
+
+/* Report any needed quiescent state for this exiting task. */
+static void exit_tasks_rcu_finish_trace(struct task_struct *t)
+{
+       WRITE_ONCE(t->trc_reader_checked, true);
+       WARN_ON_ONCE(t->trc_reader_nesting);
+       WRITE_ONCE(t->trc_reader_nesting, 0);
+       if (WARN_ON_ONCE(READ_ONCE(t->trc_reader_special.b.need_qs)))
+               rcu_read_unlock_trace_special(t, 0);
+}
+
+/**
+ * call_rcu_tasks_trace() - Queue a callback trace task-based grace period
+ * @rhp: structure to be used for queueing the RCU updates.
+ * @func: actual callback function to be invoked after the grace period
+ *
+ * The callback function will be invoked some time after a full grace
+ * period elapses, in other words after all currently executing RCU
+ * read-side critical sections have completed. call_rcu_tasks_trace()
+ * assumes that the read-side critical sections end at context switch,
+ * cond_resched_rcu_qs(), or transition to usermode execution.  As such,
+ * there are no read-side primitives analogous to rcu_read_lock() and
+ * rcu_read_unlock() because this primitive is intended to determine
+ * that all tasks have passed through a safe state, not so much for
+ * data-strcuture synchronization.
+ *
+ * See the description of call_rcu() for more detailed information on
+ * memory ordering guarantees.
+ */
+void call_rcu_tasks_trace(struct rcu_head *rhp, rcu_callback_t func)
+{
+       call_rcu_tasks_generic(rhp, func, &rcu_tasks_trace);
+}
+EXPORT_SYMBOL_GPL(call_rcu_tasks_trace);
+
+/**
+ * synchronize_rcu_tasks_trace - wait for a trace rcu-tasks grace period
+ *
+ * Control will return to the caller some time after a trace rcu-tasks
+ * grace period has elapsed, in other words after all currently
+ * executing rcu-tasks read-side critical sections have elapsed.  These
+ * read-side critical sections are delimited by calls to schedule(),
+ * cond_resched_tasks_rcu_qs(), userspace execution, and (in theory,
+ * anyway) cond_resched().
+ *
+ * This is a very specialized primitive, intended only for a few uses in
+ * tracing and other situations requiring manipulation of function preambles
+ * and profiling hooks.  The synchronize_rcu_tasks_trace() function is not
+ * (yet) intended for heavy use from multiple CPUs.
+ *
+ * See the description of synchronize_rcu() for more detailed information
+ * on memory ordering guarantees.
+ */
+void synchronize_rcu_tasks_trace(void)
+{
+       RCU_LOCKDEP_WARN(lock_is_held(&rcu_trace_lock_map), "Illegal synchronize_rcu_tasks_trace() in RCU Tasks Trace read-side critical section");
+       synchronize_rcu_tasks_generic(&rcu_tasks_trace);
+}
+EXPORT_SYMBOL_GPL(synchronize_rcu_tasks_trace);
+
+/**
+ * rcu_barrier_tasks_trace - Wait for in-flight call_rcu_tasks_trace() callbacks.
+ *
+ * Although the current implementation is guaranteed to wait, it is not
+ * obligated to, for example, if there are no pending callbacks.
+ */
+void rcu_barrier_tasks_trace(void)
+{
+       /* There is only one callback queue, so this is easy.  ;-) */
+       synchronize_rcu_tasks_trace();
+}
+EXPORT_SYMBOL_GPL(rcu_barrier_tasks_trace);
+
+static int __init rcu_spawn_tasks_trace_kthread(void)
+{
+       rcu_tasks_trace.pregp_func = rcu_tasks_trace_pregp_step;
+       rcu_tasks_trace.pertask_func = rcu_tasks_trace_pertask;
+       rcu_tasks_trace.postscan_func = rcu_tasks_trace_postscan;
+       rcu_tasks_trace.holdouts_func = check_all_holdout_tasks_trace;
+       rcu_tasks_trace.postgp_func = rcu_tasks_trace_postgp;
+       rcu_spawn_tasks_kthread_generic(&rcu_tasks_trace);
+       return 0;
+}
+core_initcall(rcu_spawn_tasks_trace_kthread);
+
+static void show_rcu_tasks_trace_gp_kthread(void)
+{
+       char buf[64];
+
+       sprintf(buf, "N%d h:%lu/%lu/%lu", atomic_read(&trc_n_readers_need_end),
+               data_race(n_heavy_reader_ofl_updates),
+               data_race(n_heavy_reader_updates),
+               data_race(n_heavy_reader_attempts));
+       show_rcu_tasks_generic_gp_kthread(&rcu_tasks_trace, buf);
+}
+
+#else /* #ifdef CONFIG_TASKS_TRACE_RCU */
+static void exit_tasks_rcu_finish_trace(struct task_struct *t) { }
+static inline void show_rcu_tasks_trace_gp_kthread(void) {}
+#endif /* #else #ifdef CONFIG_TASKS_TRACE_RCU */
+
+void show_rcu_tasks_gp_kthreads(void)
+{
+       show_rcu_tasks_classic_gp_kthread();
+       show_rcu_tasks_rude_gp_kthread();
+       show_rcu_tasks_trace_gp_kthread();
+}
+
+#else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
+static inline void rcu_tasks_bootup_oddness(void) {}
+void show_rcu_tasks_gp_kthreads(void) {}
+#endif /* #else #ifdef CONFIG_TASKS_RCU_GENERIC */
index d9a49cd6065a20936edbda1b334136ab597cde52..c716eadc7617fe4c5181d687dcec9272e045f22e 100644 (file)
 #endif
 #define MODULE_PARAM_PREFIX "rcutree."
 
+#ifndef data_race
+#define data_race(expr)                                                        \
+       ({                                                              \
+               expr;                                                   \
+       })
+#endif
+#ifndef ASSERT_EXCLUSIVE_WRITER
+#define ASSERT_EXCLUSIVE_WRITER(var) do { } while (0)
+#endif
+#ifndef ASSERT_EXCLUSIVE_ACCESS
+#define ASSERT_EXCLUSIVE_ACCESS(var) do { } while (0)
+#endif
+
 /* Data structures. */
 
 /*
@@ -75,9 +88,6 @@
  */
 #define RCU_DYNTICK_CTRL_MASK 0x1
 #define RCU_DYNTICK_CTRL_CTR  (RCU_DYNTICK_CTRL_MASK + 1)
-#ifndef rcu_eqs_special_exit
-#define rcu_eqs_special_exit() do { } while (0)
-#endif
 
 static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = {
        .dynticks_nesting = 1,
@@ -100,7 +110,7 @@ static struct rcu_state rcu_state = {
 static bool dump_tree;
 module_param(dump_tree, bool, 0444);
 /* By default, use RCU_SOFTIRQ instead of rcuc kthreads. */
-static bool use_softirq = 1;
+static bool use_softirq = true;
 module_param(use_softirq, bool, 0444);
 /* Control rcu_node-tree auto-balancing at boot time. */
 static bool rcu_fanout_exact;
@@ -225,9 +235,11 @@ void rcu_softirq_qs(void)
 
 /*
  * Record entry into an extended quiescent state.  This is only to be
- * called when not already in an extended quiescent state.
+ * called when not already in an extended quiescent state, that is,
+ * RCU is watching prior to the call to this function and is no longer
+ * watching upon return.
  */
-static void rcu_dynticks_eqs_enter(void)
+static noinstr void rcu_dynticks_eqs_enter(void)
 {
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
        int seq;
@@ -237,8 +249,9 @@ static void rcu_dynticks_eqs_enter(void)
         * critical sections, and we also must force ordering with the
         * next idle sojourn.
         */
+       rcu_dynticks_task_trace_enter();  // Before ->dynticks update!
        seq = atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks);
-       /* Better be in an extended quiescent state! */
+       // RCU is no longer watching.  Better be in extended quiescent state!
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
                     (seq & RCU_DYNTICK_CTRL_CTR));
        /* Better not have special action (TLB flush) pending! */
@@ -248,9 +261,10 @@ static void rcu_dynticks_eqs_enter(void)
 
 /*
  * Record exit from an extended quiescent state.  This is only to be
- * called from an extended quiescent state.
+ * called from an extended quiescent state, that is, RCU is not watching
+ * prior to the call to this function and is watching upon return.
  */
-static void rcu_dynticks_eqs_exit(void)
+static noinstr void rcu_dynticks_eqs_exit(void)
 {
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
        int seq;
@@ -261,13 +275,13 @@ static void rcu_dynticks_eqs_exit(void)
         * critical section.
         */
        seq = atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks);
+       // RCU is now watching.  Better not be in an extended quiescent state!
+       rcu_dynticks_task_trace_exit();  // After ->dynticks update!
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
                     !(seq & RCU_DYNTICK_CTRL_CTR));
        if (seq & RCU_DYNTICK_CTRL_MASK) {
                atomic_andnot(RCU_DYNTICK_CTRL_MASK, &rdp->dynticks);
                smp_mb__after_atomic(); /* _exit after clearing mask. */
-               /* Prefer duplicate flushes to losing a flush. */
-               rcu_eqs_special_exit();
        }
 }
 
@@ -295,7 +309,7 @@ static void rcu_dynticks_eqs_online(void)
  *
  * No ordering, as we are sampling CPU-local information.
  */
-static bool rcu_dynticks_curr_cpu_in_eqs(void)
+static __always_inline bool rcu_dynticks_curr_cpu_in_eqs(void)
 {
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
 
@@ -332,6 +346,28 @@ static bool rcu_dynticks_in_eqs_since(struct rcu_data *rdp, int snap)
        return snap != rcu_dynticks_snap(rdp);
 }
 
+/*
+ * Return true if the referenced integer is zero while the specified
+ * CPU remains within a single extended quiescent state.
+ */
+bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)
+{
+       struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
+       int snap;
+
+       // If not quiescent, force back to earlier extended quiescent state.
+       snap = atomic_read(&rdp->dynticks) & ~(RCU_DYNTICK_CTRL_MASK |
+                                              RCU_DYNTICK_CTRL_CTR);
+
+       smp_rmb(); // Order ->dynticks and *vp reads.
+       if (READ_ONCE(*vp))
+               return false;  // Non-zero, so report failure;
+       smp_rmb(); // Order *vp read and ->dynticks re-read.
+
+       // If still in the same extended quiescent state, we are good!
+       return snap == (atomic_read(&rdp->dynticks) & ~RCU_DYNTICK_CTRL_MASK);
+}
+
 /*
  * Set the special (bottom) bit of the specified CPU so that it
  * will take special action (such as flushing its TLB) on the
@@ -382,16 +418,23 @@ void rcu_momentary_dyntick_idle(void)
 EXPORT_SYMBOL_GPL(rcu_momentary_dyntick_idle);
 
 /**
- * rcu_is_cpu_rrupt_from_idle - see if interrupted from idle
+ * rcu_is_cpu_rrupt_from_idle - see if 'interrupted' from idle
  *
  * If the current CPU is idle and running at a first-level (not nested)
- * interrupt from idle, return true.  The caller must have at least
- * disabled preemption.
+ * interrupt, or directly, from idle, return true.
+ *
+ * The caller must have at least disabled IRQs.
  */
 static int rcu_is_cpu_rrupt_from_idle(void)
 {
-       /* Called only from within the scheduling-clock interrupt */
-       lockdep_assert_in_irq();
+       long nesting;
+
+       /*
+        * Usually called from the tick; but also used from smp_function_call()
+        * for expedited grace periods. This latter can result in running from
+        * the idle task, instead of an actual IPI.
+        */
+       lockdep_assert_irqs_disabled();
 
        /* Check for counter underflows */
        RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nesting) < 0,
@@ -400,9 +443,15 @@ static int rcu_is_cpu_rrupt_from_idle(void)
                         "RCU dynticks_nmi_nesting counter underflow/zero!");
 
        /* Are we at first interrupt nesting level? */
-       if (__this_cpu_read(rcu_data.dynticks_nmi_nesting) != 1)
+       nesting = __this_cpu_read(rcu_data.dynticks_nmi_nesting);
+       if (nesting > 1)
                return false;
 
+       /*
+        * If we're not in an interrupt, we must be in the idle task!
+        */
+       WARN_ON_ONCE(!nesting && !is_idle_task(current));
+
        /* Does CPU appear to be idle from an RCU standpoint? */
        return __this_cpu_read(rcu_data.dynticks_nesting) == 0;
 }
@@ -562,7 +611,7 @@ EXPORT_SYMBOL_GPL(rcutorture_get_gp_data);
  * the possibility of usermode upcalls having messed up our count
  * of interrupt nesting level during the prior busy period.
  */
-static void rcu_eqs_enter(bool user)
+static noinstr void rcu_eqs_enter(bool user)
 {
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
 
@@ -571,19 +620,24 @@ static void rcu_eqs_enter(bool user)
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
                     rdp->dynticks_nesting == 0);
        if (rdp->dynticks_nesting != 1) {
+               // RCU will still be watching, so just do accounting and leave.
                rdp->dynticks_nesting--;
                return;
        }
 
        lockdep_assert_irqs_disabled();
+       instrumentation_begin();
        trace_rcu_dyntick(TPS("Start"), rdp->dynticks_nesting, 0, atomic_read(&rdp->dynticks));
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !user && !is_idle_task(current));
        rdp = this_cpu_ptr(&rcu_data);
        do_nocb_deferred_wakeup(rdp);
        rcu_prepare_for_idle();
        rcu_preempt_deferred_qs(current);
+       instrumentation_end();
        WRITE_ONCE(rdp->dynticks_nesting, 0); /* Avoid irq-access tearing. */
+       // RCU is watching here ...
        rcu_dynticks_eqs_enter();
+       // ... but is no longer watching here.
        rcu_dynticks_task_enter();
 }
 
@@ -616,23 +670,25 @@ void rcu_idle_enter(void)
  * If you add or remove a call to rcu_user_enter(), be sure to test with
  * CONFIG_RCU_EQS_DEBUG=y.
  */
-void rcu_user_enter(void)
+noinstr void rcu_user_enter(void)
 {
        lockdep_assert_irqs_disabled();
        rcu_eqs_enter(true);
 }
 #endif /* CONFIG_NO_HZ_FULL */
 
-/*
+/**
+ * rcu_nmi_exit - inform RCU of exit from NMI context
+ *
  * If we are returning from the outermost NMI handler that interrupted an
  * RCU-idle period, update rdp->dynticks and rdp->dynticks_nmi_nesting
  * to let the RCU grace-period handling know that the CPU is back to
  * being RCU-idle.
  *
- * If you add or remove a call to rcu_nmi_exit_common(), be sure to test
+ * If you add or remove a call to rcu_nmi_exit(), be sure to test
  * with CONFIG_RCU_EQS_DEBUG=y.
  */
-static __always_inline void rcu_nmi_exit_common(bool irq)
+noinstr void rcu_nmi_exit(void)
 {
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
 
@@ -649,37 +705,32 @@ static __always_inline void rcu_nmi_exit_common(bool irq)
         * leave it in non-RCU-idle state.
         */
        if (rdp->dynticks_nmi_nesting != 1) {
+               instrumentation_begin();
                trace_rcu_dyntick(TPS("--="), rdp->dynticks_nmi_nesting, rdp->dynticks_nmi_nesting - 2,
                                  atomic_read(&rdp->dynticks));
                WRITE_ONCE(rdp->dynticks_nmi_nesting, /* No store tearing. */
                           rdp->dynticks_nmi_nesting - 2);
+               instrumentation_end();
                return;
        }
 
+       instrumentation_begin();
        /* This NMI interrupted an RCU-idle CPU, restore RCU-idleness. */
        trace_rcu_dyntick(TPS("Startirq"), rdp->dynticks_nmi_nesting, 0, atomic_read(&rdp->dynticks));
        WRITE_ONCE(rdp->dynticks_nmi_nesting, 0); /* Avoid store tearing. */
 
-       if (irq)
+       if (!in_nmi())
                rcu_prepare_for_idle();
+       instrumentation_end();
 
+       // RCU is watching here ...
        rcu_dynticks_eqs_enter();
+       // ... but is no longer watching here.
 
-       if (irq)
+       if (!in_nmi())
                rcu_dynticks_task_enter();
 }
 
-/**
- * rcu_nmi_exit - inform RCU of exit from NMI context
- *
- * If you add or remove a call to rcu_nmi_exit(), be sure to test
- * with CONFIG_RCU_EQS_DEBUG=y.
- */
-void rcu_nmi_exit(void)
-{
-       rcu_nmi_exit_common(false);
-}
-
 /**
  * rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle
  *
@@ -699,12 +750,52 @@ void rcu_nmi_exit(void)
  * If you add or remove a call to rcu_irq_exit(), be sure to test with
  * CONFIG_RCU_EQS_DEBUG=y.
  */
-void rcu_irq_exit(void)
+void noinstr rcu_irq_exit(void)
+{
+       lockdep_assert_irqs_disabled();
+       rcu_nmi_exit();
+}
+
+/**
+ * rcu_irq_exit_preempt - Inform RCU that current CPU is exiting irq
+ *                       towards in kernel preemption
+ *
+ * Same as rcu_irq_exit() but has a sanity check that scheduling is safe
+ * from RCU point of view. Invoked from return from interrupt before kernel
+ * preemption.
+ */
+void rcu_irq_exit_preempt(void)
 {
        lockdep_assert_irqs_disabled();
-       rcu_nmi_exit_common(true);
+       rcu_nmi_exit();
+
+       RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nesting) <= 0,
+                        "RCU dynticks_nesting counter underflow/zero!");
+       RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nmi_nesting) !=
+                        DYNTICK_IRQ_NONIDLE,
+                        "Bad RCU  dynticks_nmi_nesting counter\n");
+       RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
+                        "RCU in extended quiescent state!");
 }
 
+#ifdef CONFIG_PROVE_RCU
+/**
+ * rcu_irq_exit_check_preempt - Validate that scheduling is possible
+ */
+void rcu_irq_exit_check_preempt(void)
+{
+       lockdep_assert_irqs_disabled();
+
+       RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nesting) <= 0,
+                        "RCU dynticks_nesting counter underflow/zero!");
+       RCU_LOCKDEP_WARN(__this_cpu_read(rcu_data.dynticks_nmi_nesting) !=
+                        DYNTICK_IRQ_NONIDLE,
+                        "Bad RCU  dynticks_nmi_nesting counter\n");
+       RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
+                        "RCU in extended quiescent state!");
+}
+#endif /* #ifdef CONFIG_PROVE_RCU */
+
 /*
  * Wrapper for rcu_irq_exit() where interrupts are enabled.
  *
@@ -728,7 +819,7 @@ void rcu_irq_exit_irqson(void)
  * allow for the possibility of usermode upcalls messing up our count of
  * interrupt nesting level during the busy period that is just now starting.
  */
-static void rcu_eqs_exit(bool user)
+static void noinstr rcu_eqs_exit(bool user)
 {
        struct rcu_data *rdp;
        long oldval;
@@ -738,17 +829,22 @@ static void rcu_eqs_exit(bool user)
        oldval = rdp->dynticks_nesting;
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && oldval < 0);
        if (oldval) {
+               // RCU was already watching, so just do accounting and leave.
                rdp->dynticks_nesting++;
                return;
        }
        rcu_dynticks_task_exit();
+       // RCU is not watching here ...
        rcu_dynticks_eqs_exit();
+       // ... but is watching here.
+       instrumentation_begin();
        rcu_cleanup_after_idle();
        trace_rcu_dyntick(TPS("End"), rdp->dynticks_nesting, 1, atomic_read(&rdp->dynticks));
        WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !user && !is_idle_task(current));
        WRITE_ONCE(rdp->dynticks_nesting, 1);
        WARN_ON_ONCE(rdp->dynticks_nmi_nesting);
        WRITE_ONCE(rdp->dynticks_nmi_nesting, DYNTICK_IRQ_NONIDLE);
+       instrumentation_end();
 }
 
 /**
@@ -779,14 +875,75 @@ void rcu_idle_exit(void)
  * If you add or remove a call to rcu_user_exit(), be sure to test with
  * CONFIG_RCU_EQS_DEBUG=y.
  */
-void rcu_user_exit(void)
+void noinstr rcu_user_exit(void)
 {
        rcu_eqs_exit(1);
 }
+
+/**
+ * __rcu_irq_enter_check_tick - Enable scheduler tick on CPU if RCU needs it.
+ *
+ * The scheduler tick is not normally enabled when CPUs enter the kernel
+ * from nohz_full userspace execution.  After all, nohz_full userspace
+ * execution is an RCU quiescent state and the time executing in the kernel
+ * is quite short.  Except of course when it isn't.  And it is not hard to
+ * cause a large system to spend tens of seconds or even minutes looping
+ * in the kernel, which can cause a number of problems, include RCU CPU
+ * stall warnings.
+ *
+ * Therefore, if a nohz_full CPU fails to report a quiescent state
+ * in a timely manner, the RCU grace-period kthread sets that CPU's
+ * ->rcu_urgent_qs flag with the expectation that the next interrupt or
+ * exception will invoke this function, which will turn on the scheduler
+ * tick, which will enable RCU to detect that CPU's quiescent states,
+ * for example, due to cond_resched() calls in CONFIG_PREEMPT=n kernels.
+ * The tick will be disabled once a quiescent state is reported for
+ * this CPU.
+ *
+ * Of course, in carefully tuned systems, there might never be an
+ * interrupt or exception.  In that case, the RCU grace-period kthread
+ * will eventually cause one to happen.  However, in less carefully
+ * controlled environments, this function allows RCU to get what it
+ * needs without creating otherwise useless interruptions.
+ */
+void __rcu_irq_enter_check_tick(void)
+{
+       struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
+
+        // Enabling the tick is unsafe in NMI handlers.
+       if (WARN_ON_ONCE(in_nmi()))
+               return;
+
+       RCU_LOCKDEP_WARN(rcu_dynticks_curr_cpu_in_eqs(),
+                        "Illegal rcu_irq_enter_check_tick() from extended quiescent state");
+
+       if (!tick_nohz_full_cpu(rdp->cpu) ||
+           !READ_ONCE(rdp->rcu_urgent_qs) ||
+           READ_ONCE(rdp->rcu_forced_tick)) {
+               // RCU doesn't need nohz_full help from this CPU, or it is
+               // already getting that help.
+               return;
+       }
+
+       // We get here only when not in an extended quiescent state and
+       // from interrupts (as opposed to NMIs).  Therefore, (1) RCU is
+       // already watching and (2) The fact that we are in an interrupt
+       // handler and that the rcu_node lock is an irq-disabled lock
+       // prevents self-deadlock.  So we can safely recheck under the lock.
+       // Note that the nohz_full state currently cannot change.
+       raw_spin_lock_rcu_node(rdp->mynode);
+       if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
+               // A nohz_full CPU is in the kernel and RCU needs a
+               // quiescent state.  Turn on the tick!
+               WRITE_ONCE(rdp->rcu_forced_tick, true);
+               tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
+       }
+       raw_spin_unlock_rcu_node(rdp->mynode);
+}
 #endif /* CONFIG_NO_HZ_FULL */
 
 /**
- * rcu_nmi_enter_common - inform RCU of entry to NMI context
+ * rcu_nmi_enter - inform RCU of entry to NMI context
  * @irq: Is this call from rcu_irq_enter?
  *
  * If the CPU was idle from RCU's viewpoint, update rdp->dynticks and
@@ -795,10 +952,10 @@ void rcu_user_exit(void)
  * long as the nesting level does not overflow an int.  (You will probably
  * run out of stack space first.)
  *
- * If you add or remove a call to rcu_nmi_enter_common(), be sure to test
+ * If you add or remove a call to rcu_nmi_enter(), be sure to test
  * with CONFIG_RCU_EQS_DEBUG=y.
  */
-static __always_inline void rcu_nmi_enter_common(bool irq)
+noinstr void rcu_nmi_enter(void)
 {
        long incby = 2;
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
@@ -816,44 +973,32 @@ static __always_inline void rcu_nmi_enter_common(bool irq)
         */
        if (rcu_dynticks_curr_cpu_in_eqs()) {
 
-               if (irq)
+               if (!in_nmi())
                        rcu_dynticks_task_exit();
 
+               // RCU is not watching here ...
                rcu_dynticks_eqs_exit();
+               // ... but is watching here.
 
-               if (irq)
+               if (!in_nmi())
                        rcu_cleanup_after_idle();
 
                incby = 1;
-       } else if (irq && tick_nohz_full_cpu(rdp->cpu) &&
-                  rdp->dynticks_nmi_nesting == DYNTICK_IRQ_NONIDLE &&
-                  READ_ONCE(rdp->rcu_urgent_qs) &&
-                  !READ_ONCE(rdp->rcu_forced_tick)) {
-               raw_spin_lock_rcu_node(rdp->mynode);
-               // Recheck under lock.
-               if (rdp->rcu_urgent_qs && !rdp->rcu_forced_tick) {
-                       WRITE_ONCE(rdp->rcu_forced_tick, true);
-                       tick_dep_set_cpu(rdp->cpu, TICK_DEP_BIT_RCU);
-               }
-               raw_spin_unlock_rcu_node(rdp->mynode);
+       } else if (!in_nmi()) {
+               instrumentation_begin();
+               rcu_irq_enter_check_tick();
+               instrumentation_end();
        }
+       instrumentation_begin();
        trace_rcu_dyntick(incby == 1 ? TPS("Endirq") : TPS("++="),
                          rdp->dynticks_nmi_nesting,
                          rdp->dynticks_nmi_nesting + incby, atomic_read(&rdp->dynticks));
+       instrumentation_end();
        WRITE_ONCE(rdp->dynticks_nmi_nesting, /* Prevent store tearing. */
                   rdp->dynticks_nmi_nesting + incby);
        barrier();
 }
 
-/**
- * rcu_nmi_enter - inform RCU of entry to NMI context
- */
-void rcu_nmi_enter(void)
-{
-       rcu_nmi_enter_common(false);
-}
-NOKPROBE_SYMBOL(rcu_nmi_enter);
-
 /**
  * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle
  *
@@ -876,10 +1021,10 @@ NOKPROBE_SYMBOL(rcu_nmi_enter);
  * If you add or remove a call to rcu_irq_enter(), be sure to test with
  * CONFIG_RCU_EQS_DEBUG=y.
  */
-void rcu_irq_enter(void)
+noinstr void rcu_irq_enter(void)
 {
        lockdep_assert_irqs_disabled();
-       rcu_nmi_enter_common(true);
+       rcu_nmi_enter();
 }
 
 /*
@@ -913,6 +1058,11 @@ static void rcu_disable_urgency_upon_qs(struct rcu_data *rdp)
        }
 }
 
+noinstr bool __rcu_is_watching(void)
+{
+       return !rcu_dynticks_curr_cpu_in_eqs();
+}
+
 /**
  * rcu_is_watching - see if RCU thinks that the current CPU is not idle
  *
@@ -921,7 +1071,7 @@ static void rcu_disable_urgency_upon_qs(struct rcu_data *rdp)
  * if the current CPU is not in its idle loop or is in an interrupt or
  * NMI handler, return true.
  */
-bool notrace rcu_is_watching(void)
+bool rcu_is_watching(void)
 {
        bool ret;
 
@@ -973,12 +1123,12 @@ bool rcu_lockdep_current_cpu_online(void)
 
        if (in_nmi() || !rcu_scheduler_fully_active)
                return true;
-       preempt_disable();
+       preempt_disable_notrace();
        rdp = this_cpu_ptr(&rcu_data);
        rnp = rdp->mynode;
        if (rdp->grpmask & rcu_rnp_online_cpus(rnp))
                ret = true;
-       preempt_enable();
+       preempt_enable_notrace();
        return ret;
 }
 EXPORT_SYMBOL_GPL(rcu_lockdep_current_cpu_online);
@@ -1217,7 +1367,7 @@ static bool rcu_start_this_gp(struct rcu_node *rnp_start, struct rcu_data *rdp,
                trace_rcu_this_gp(rnp, rdp, gp_seq_req, TPS("NoGPkthread"));
                goto unlock_out;
        }
-       trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq, TPS("newreq"));
+       trace_rcu_grace_period(rcu_state.name, data_race(rcu_state.gp_seq), TPS("newreq"));
        ret = true;  /* Caller must wake GP kthread. */
 unlock_out:
        /* Push furthest requested GP to leaf node and rcu_data structure. */
@@ -1473,6 +1623,31 @@ static void rcu_gp_slow(int delay)
                schedule_timeout_uninterruptible(delay);
 }
 
+static unsigned long sleep_duration;
+
+/* Allow rcutorture to stall the grace-period kthread. */
+void rcu_gp_set_torture_wait(int duration)
+{
+       if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST) && duration > 0)
+               WRITE_ONCE(sleep_duration, duration);
+}
+EXPORT_SYMBOL_GPL(rcu_gp_set_torture_wait);
+
+/* Actually implement the aforementioned wait. */
+static void rcu_gp_torture_wait(void)
+{
+       unsigned long duration;
+
+       if (!IS_ENABLED(CONFIG_RCU_TORTURE_TEST))
+               return;
+       duration = xchg(&sleep_duration, 0UL);
+       if (duration > 0) {
+               pr_alert("%s: Waiting %lu jiffies\n", __func__, duration);
+               schedule_timeout_uninterruptible(duration);
+               pr_alert("%s: Wait complete\n", __func__);
+       }
+}
+
 /*
  * Initialize a new grace period.  Return false if no grace period required.
  */
@@ -1506,6 +1681,7 @@ static bool rcu_gp_init(void)
        record_gp_stall_check_time();
        /* Record GP times before starting GP, hence rcu_seq_start(). */
        rcu_seq_start(&rcu_state.gp_seq);
+       ASSERT_EXCLUSIVE_WRITER(rcu_state.gp_seq);
        trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq, TPS("start"));
        raw_spin_unlock_irq_rcu_node(rnp);
 
@@ -1611,12 +1787,16 @@ static bool rcu_gp_fqs_check_wake(int *gfp)
 {
        struct rcu_node *rnp = rcu_get_root();
 
-       /* Someone like call_rcu() requested a force-quiescent-state scan. */
+       // If under overload conditions, force an immediate FQS scan.
+       if (*gfp & RCU_GP_FLAG_OVLD)
+               return true;
+
+       // Someone like call_rcu() requested a force-quiescent-state scan.
        *gfp = READ_ONCE(rcu_state.gp_flags);
        if (*gfp & RCU_GP_FLAG_FQS)
                return true;
 
-       /* The current grace period has completed. */
+       // The current grace period has completed.
        if (!READ_ONCE(rnp->qsmask) && !rcu_preempt_blocked_readers_cgp(rnp))
                return true;
 
@@ -1654,13 +1834,15 @@ static void rcu_gp_fqs(bool first_time)
 static void rcu_gp_fqs_loop(void)
 {
        bool first_gp_fqs;
-       int gf;
+       int gf = 0;
        unsigned long j;
        int ret;
        struct rcu_node *rnp = rcu_get_root();
 
        first_gp_fqs = true;
        j = READ_ONCE(jiffies_till_first_fqs);
+       if (rcu_state.cbovld)
+               gf = RCU_GP_FLAG_OVLD;
        ret = 0;
        for (;;) {
                if (!ret) {
@@ -1673,6 +1855,7 @@ static void rcu_gp_fqs_loop(void)
                rcu_state.gp_state = RCU_GP_WAIT_FQS;
                ret = swait_event_idle_timeout_exclusive(
                                rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j);
+               rcu_gp_torture_wait();
                rcu_state.gp_state = RCU_GP_DOING_FQS;
                /* Locking provides needed memory barriers. */
                /* If grace period done, leave loop. */
@@ -1680,12 +1863,16 @@ static void rcu_gp_fqs_loop(void)
                    !rcu_preempt_blocked_readers_cgp(rnp))
                        break;
                /* If time for quiescent-state forcing, do it. */
-               if (ULONG_CMP_GE(jiffies, rcu_state.jiffies_force_qs) ||
+               if (!time_after(rcu_state.jiffies_force_qs, jiffies) ||
                    (gf & RCU_GP_FLAG_FQS)) {
                        trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq,
                                               TPS("fqsstart"));
                        rcu_gp_fqs(first_gp_fqs);
-                       first_gp_fqs = false;
+                       gf = 0;
+                       if (first_gp_fqs) {
+                               first_gp_fqs = false;
+                               gf = rcu_state.cbovld ? RCU_GP_FLAG_OVLD : 0;
+                       }
                        trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq,
                                               TPS("fqsend"));
                        cond_resched_tasks_rcu_qs();
@@ -1705,6 +1892,7 @@ static void rcu_gp_fqs_loop(void)
                                j = 1;
                        else
                                j = rcu_state.jiffies_force_qs - j;
+                       gf = 0;
                }
        }
 }
@@ -1781,6 +1969,7 @@ static void rcu_gp_cleanup(void)
        /* Declare grace period done, trace first to use old GP number. */
        trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq, TPS("end"));
        rcu_seq_end(&rcu_state.gp_seq);
+       ASSERT_EXCLUSIVE_WRITER(rcu_state.gp_seq);
        rcu_state.gp_state = RCU_GP_IDLE;
        /* Check for GP requests since above loop. */
        rdp = this_cpu_ptr(&rcu_data);
@@ -1821,6 +2010,7 @@ static int __noreturn rcu_gp_kthread(void *unused)
                        swait_event_idle_exclusive(rcu_state.gp_wq,
                                         READ_ONCE(rcu_state.gp_flags) &
                                         RCU_GP_FLAG_INIT);
+                       rcu_gp_torture_wait();
                        rcu_state.gp_state = RCU_GP_DONE_GPS;
                        /* Locking provides needed memory barrier. */
                        if (rcu_gp_init())
@@ -2811,6 +3001,8 @@ struct kfree_rcu_cpu {
        struct delayed_work monitor_work;
        bool monitor_todo;
        bool initialized;
+       // Number of objects for which GP not started
+       int count;
 };
 
 static DEFINE_PER_CPU(struct kfree_rcu_cpu, krc);
@@ -2924,6 +3116,8 @@ static inline bool queue_kfree_rcu_work(struct kfree_rcu_cpu *krcp)
                                krcp->head = NULL;
                        }
 
+                       WRITE_ONCE(krcp->count, 0);
+
                        /*
                         * One work is per one batch, so there are two "free channels",
                         * "bhead_free" and "head_free" the batch can handle. It can be
@@ -3060,6 +3254,8 @@ void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func)
                krcp->head = head;
        }
 
+       WRITE_ONCE(krcp->count, krcp->count + 1);
+
        // Set timer to drain after KFREE_DRAIN_JIFFIES.
        if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING &&
            !krcp->monitor_todo) {
@@ -3074,6 +3270,56 @@ unlock_return:
 }
 EXPORT_SYMBOL_GPL(kfree_call_rcu);
 
+static unsigned long
+kfree_rcu_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+       int cpu;
+       unsigned long count = 0;
+
+       /* Snapshot count of all CPUs */
+       for_each_online_cpu(cpu) {
+               struct kfree_rcu_cpu *krcp = per_cpu_ptr(&krc, cpu);
+
+               count += READ_ONCE(krcp->count);
+       }
+
+       return count;
+}
+
+static unsigned long
+kfree_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
+{
+       int cpu, freed = 0;
+       unsigned long flags;
+
+       for_each_online_cpu(cpu) {
+               int count;
+               struct kfree_rcu_cpu *krcp = per_cpu_ptr(&krc, cpu);
+
+               count = krcp->count;
+               spin_lock_irqsave(&krcp->lock, flags);
+               if (krcp->monitor_todo)
+                       kfree_rcu_drain_unlock(krcp, flags);
+               else
+                       spin_unlock_irqrestore(&krcp->lock, flags);
+
+               sc->nr_to_scan -= count;
+               freed += count;
+
+               if (sc->nr_to_scan <= 0)
+                       break;
+       }
+
+       return freed;
+}
+
+static struct shrinker kfree_rcu_shrinker = {
+       .count_objects = kfree_rcu_shrink_count,
+       .scan_objects = kfree_rcu_shrink_scan,
+       .batch = 0,
+       .seeks = DEFAULT_SEEKS,
+};
+
 void __init kfree_rcu_scheduler_running(void)
 {
        int cpu;
@@ -3599,6 +3845,7 @@ void rcu_cpu_starting(unsigned int cpu)
        nbits = bitmap_weight(&oldmask, BITS_PER_LONG);
        /* Allow lockless access for expedited grace periods. */
        smp_store_release(&rcu_state.ncpus, rcu_state.ncpus + nbits); /* ^^^ */
+       ASSERT_EXCLUSIVE_WRITER(rcu_state.ncpus);
        rcu_gpnum_ovf(rnp, rdp); /* Offline-induced counter wrap? */
        rdp->rcu_onl_gp_seq = READ_ONCE(rcu_state.gp_seq);
        rdp->rcu_onl_gp_flags = READ_ONCE(rcu_state.gp_flags);
@@ -3994,6 +4241,8 @@ static void __init kfree_rcu_batch_init(void)
                INIT_DELAYED_WORK(&krcp->monitor_work, kfree_rcu_monitor);
                krcp->initialized = true;
        }
+       if (register_shrinker(&kfree_rcu_shrinker))
+               pr_err("Failed to register kfree_rcu() shrinker!\n");
 }
 
 void __init rcu_init(void)
index 9dc2ec021da523a4d418034e044f3ce3df1b9a6a..43991a40b0849dea719a11b078c92a434038bef9 100644 (file)
@@ -359,6 +359,7 @@ struct rcu_state {
 /* Values for rcu_state structure's gp_flags field. */
 #define RCU_GP_FLAG_INIT 0x1   /* Need grace-period initialization. */
 #define RCU_GP_FLAG_FQS  0x2   /* Need grace-period quiescent-state forcing. */
+#define RCU_GP_FLAG_OVLD 0x4   /* Experiencing callback overload. */
 
 /* Values for rcu_state structure's gp_state field. */
 #define RCU_GP_IDLE     0      /* Initial state and no GP in progress. */
@@ -454,6 +455,8 @@ static void rcu_bind_gp_kthread(void);
 static bool rcu_nohz_full_cpu(void);
 static void rcu_dynticks_task_enter(void);
 static void rcu_dynticks_task_exit(void);
+static void rcu_dynticks_task_trace_enter(void);
+static void rcu_dynticks_task_trace_exit(void);
 
 /* Forward declarations for tree_stall.h */
 static void record_gp_stall_check_time(void);
index 1a617b9dffb0534776ff32ce879bb685d7278ea9..72952edad1e4b168ccfbfd6b0dc3a900966f3728 100644 (file)
@@ -150,7 +150,7 @@ static void __maybe_unused sync_exp_reset_tree(void)
 static bool sync_rcu_exp_done(struct rcu_node *rnp)
 {
        raw_lockdep_assert_held_rcu_node(rnp);
-       return rnp->exp_tasks == NULL &&
+       return READ_ONCE(rnp->exp_tasks) == NULL &&
               READ_ONCE(rnp->expmask) == 0;
 }
 
@@ -373,7 +373,7 @@ static void sync_rcu_exp_select_node_cpus(struct work_struct *wp)
         * until such time as the ->expmask bits are cleared.
         */
        if (rcu_preempt_has_tasks(rnp))
-               rnp->exp_tasks = rnp->blkd_tasks.next;
+               WRITE_ONCE(rnp->exp_tasks, rnp->blkd_tasks.next);
        raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
 
        /* IPI the remaining CPUs for expedited quiescent state. */
@@ -542,8 +542,8 @@ static void synchronize_rcu_expedited_wait(void)
                }
                pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n",
                        jiffies - jiffies_start, rcu_state.expedited_sequence,
-                       READ_ONCE(rnp_root->expmask),
-                       ".T"[!!rnp_root->exp_tasks]);
+                       data_race(rnp_root->expmask),
+                       ".T"[!!data_race(rnp_root->exp_tasks)]);
                if (ndetected) {
                        pr_err("blocking rcu_node structures:");
                        rcu_for_each_node_breadth_first(rnp) {
@@ -553,8 +553,8 @@ static void synchronize_rcu_expedited_wait(void)
                                        continue;
                                pr_cont(" l=%u:%d-%d:%#lx/%c",
                                        rnp->level, rnp->grplo, rnp->grphi,
-                                       READ_ONCE(rnp->expmask),
-                                       ".T"[!!rnp->exp_tasks]);
+                                       data_race(rnp->expmask),
+                                       ".T"[!!data_race(rnp->exp_tasks)]);
                        }
                        pr_cont("\n");
                }
@@ -639,6 +639,7 @@ static void wait_rcu_exp_gp(struct work_struct *wp)
  */
 static void rcu_exp_handler(void *unused)
 {
+       int depth = rcu_preempt_depth();
        unsigned long flags;
        struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
        struct rcu_node *rnp = rdp->mynode;
@@ -649,7 +650,7 @@ static void rcu_exp_handler(void *unused)
         * critical section.  If also enabled or idle, immediately
         * report the quiescent state, otherwise defer.
         */
-       if (!rcu_preempt_depth()) {
+       if (!depth) {
                if (!(preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK)) ||
                    rcu_dynticks_curr_cpu_in_eqs()) {
                        rcu_report_exp_rdp(rdp);
@@ -673,7 +674,7 @@ static void rcu_exp_handler(void *unused)
         * can have caused this quiescent state to already have been
         * reported, so we really do need to check ->expmask.
         */
-       if (rcu_preempt_depth() > 0) {
+       if (depth > 0) {
                raw_spin_lock_irqsave_rcu_node(rnp, flags);
                if (rnp->expmask & rdp->grpmask) {
                        rdp->exp_deferred_qs = true;
@@ -683,30 +684,8 @@ static void rcu_exp_handler(void *unused)
                return;
        }
 
-       /*
-        * The final and least likely case is where the interrupted
-        * code was just about to or just finished exiting the RCU-preempt
-        * read-side critical section, and no, we can't tell which.
-        * So either way, set ->deferred_qs to flag later code that
-        * a quiescent state is required.
-        *
-        * If the CPU is fully enabled (or if some buggy RCU-preempt
-        * read-side critical section is being used from idle), just
-        * invoke rcu_preempt_deferred_qs() to immediately report the
-        * quiescent state.  We cannot use rcu_read_unlock_special()
-        * because we are in an interrupt handler, which will cause that
-        * function to take an early exit without doing anything.
-        *
-        * Otherwise, force a context switch after the CPU enables everything.
-        */
-       rdp->exp_deferred_qs = true;
-       if (!(preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK)) ||
-           WARN_ON_ONCE(rcu_dynticks_curr_cpu_in_eqs())) {
-               rcu_preempt_deferred_qs(t);
-       } else {
-               set_tsk_need_resched(t);
-               set_preempt_need_resched();
-       }
+       // Finally, negative nesting depth should not happen.
+       WARN_ON_ONCE(1);
 }
 
 /* PREEMPTION=y, so no PREEMPTION=n expedited grace period to clean up after. */
@@ -721,17 +700,20 @@ static void sync_sched_exp_online_cleanup(int cpu)
  */
 static int rcu_print_task_exp_stall(struct rcu_node *rnp)
 {
-       struct task_struct *t;
+       unsigned long flags;
        int ndetected = 0;
+       struct task_struct *t;
 
-       if (!rnp->exp_tasks)
+       if (!READ_ONCE(rnp->exp_tasks))
                return 0;
+       raw_spin_lock_irqsave_rcu_node(rnp, flags);
        t = list_entry(rnp->exp_tasks->prev,
                       struct task_struct, rcu_node_entry);
        list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
                pr_cont(" P%d", t->pid);
                ndetected++;
        }
+       raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
        return ndetected;
 }
 
index 097635c41135da1954de4b1fc5fc0efc676753f4..352223664ebdc769bf1c171885d292e31a9a105b 100644 (file)
@@ -226,7 +226,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)
                WARN_ON_ONCE(rnp->completedqs == rnp->gp_seq);
        }
        if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD))
-               rnp->exp_tasks = &t->rcu_node_entry;
+               WRITE_ONCE(rnp->exp_tasks, &t->rcu_node_entry);
        WARN_ON_ONCE(!(blkd_state & RCU_GP_BLKD) !=
                     !(rnp->qsmask & rdp->grpmask));
        WARN_ON_ONCE(!(blkd_state & RCU_EXP_BLKD) !=
@@ -331,6 +331,7 @@ void rcu_note_context_switch(bool preempt)
        rcu_qs();
        if (rdp->exp_deferred_qs)
                rcu_report_exp_rdp(rdp);
+       rcu_tasks_qs(current, preempt);
        trace_rcu_utilization(TPS("End context switch"));
 }
 EXPORT_SYMBOL_GPL(rcu_note_context_switch);
@@ -345,9 +346,7 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp)
        return READ_ONCE(rnp->gp_tasks) != NULL;
 }
 
-/* Bias and limit values for ->rcu_read_lock_nesting. */
-#define RCU_NEST_BIAS INT_MAX
-#define RCU_NEST_NMAX (-INT_MAX / 2)
+/* limit value for ->rcu_read_lock_nesting. */
 #define RCU_NEST_PMAX (INT_MAX / 2)
 
 static void rcu_preempt_read_enter(void)
@@ -355,9 +354,9 @@ static void rcu_preempt_read_enter(void)
        current->rcu_read_lock_nesting++;
 }
 
-static void rcu_preempt_read_exit(void)
+static int rcu_preempt_read_exit(void)
 {
-       current->rcu_read_lock_nesting--;
+       return --current->rcu_read_lock_nesting;
 }
 
 static void rcu_preempt_depth_set(int val)
@@ -390,21 +389,15 @@ void __rcu_read_unlock(void)
 {
        struct task_struct *t = current;
 
-       if (rcu_preempt_depth() != 1) {
-               rcu_preempt_read_exit();
-       } else {
+       if (rcu_preempt_read_exit() == 0) {
                barrier();  /* critical section before exit code. */
-               rcu_preempt_depth_set(-RCU_NEST_BIAS);
-               barrier();  /* assign before ->rcu_read_unlock_special load */
                if (unlikely(READ_ONCE(t->rcu_read_unlock_special.s)))
                        rcu_read_unlock_special(t);
-               barrier();  /* ->rcu_read_unlock_special load before assign */
-               rcu_preempt_depth_set(0);
        }
        if (IS_ENABLED(CONFIG_PROVE_LOCKING)) {
                int rrln = rcu_preempt_depth();
 
-               WARN_ON_ONCE(rrln < 0 && rrln > RCU_NEST_NMAX);
+               WARN_ON_ONCE(rrln < 0 || rrln > RCU_NEST_PMAX);
        }
 }
 EXPORT_SYMBOL_GPL(__rcu_read_unlock);
@@ -500,12 +493,12 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags)
                if (&t->rcu_node_entry == rnp->gp_tasks)
                        WRITE_ONCE(rnp->gp_tasks, np);
                if (&t->rcu_node_entry == rnp->exp_tasks)
-                       rnp->exp_tasks = np;
+                       WRITE_ONCE(rnp->exp_tasks, np);
                if (IS_ENABLED(CONFIG_RCU_BOOST)) {
                        /* Snapshot ->boost_mtx ownership w/rnp->lock held. */
                        drop_boost_mutex = rt_mutex_owner(&rnp->boost_mtx) == t;
                        if (&t->rcu_node_entry == rnp->boost_tasks)
-                               rnp->boost_tasks = np;
+                               WRITE_ONCE(rnp->boost_tasks, np);
                }
 
                /*
@@ -556,7 +549,7 @@ static bool rcu_preempt_need_deferred_qs(struct task_struct *t)
 {
        return (__this_cpu_read(rcu_data.exp_deferred_qs) ||
                READ_ONCE(t->rcu_read_unlock_special.s)) &&
-              rcu_preempt_depth() <= 0;
+              rcu_preempt_depth() == 0;
 }
 
 /*
@@ -569,16 +562,11 @@ static bool rcu_preempt_need_deferred_qs(struct task_struct *t)
 static void rcu_preempt_deferred_qs(struct task_struct *t)
 {
        unsigned long flags;
-       bool couldrecurse = rcu_preempt_depth() >= 0;
 
        if (!rcu_preempt_need_deferred_qs(t))
                return;
-       if (couldrecurse)
-               rcu_preempt_depth_set(rcu_preempt_depth() - RCU_NEST_BIAS);
        local_irq_save(flags);
        rcu_preempt_deferred_qs_irqrestore(t, flags);
-       if (couldrecurse)
-               rcu_preempt_depth_set(rcu_preempt_depth() + RCU_NEST_BIAS);
 }
 
 /*
@@ -615,19 +603,18 @@ static void rcu_read_unlock_special(struct task_struct *t)
                struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
                struct rcu_node *rnp = rdp->mynode;
 
-               exp = (t->rcu_blocked_node && t->rcu_blocked_node->exp_tasks) ||
-                     (rdp->grpmask & READ_ONCE(rnp->expmask)) ||
-                     tick_nohz_full_cpu(rdp->cpu);
+               exp = (t->rcu_blocked_node &&
+                      READ_ONCE(t->rcu_blocked_node->exp_tasks)) ||
+                     (rdp->grpmask & READ_ONCE(rnp->expmask));
                // Need to defer quiescent state until everything is enabled.
-               if (irqs_were_disabled && use_softirq &&
-                   (in_interrupt() ||
-                    (exp && !t->rcu_read_unlock_special.b.deferred_qs))) {
-                       // Using softirq, safe to awaken, and we get
-                       // no help from enabling irqs, unlike bh/preempt.
+               if (use_softirq && (in_irq() || (exp && !irqs_were_disabled))) {
+                       // Using softirq, safe to awaken, and either the
+                       // wakeup is free or there is an expedited GP.
                        raise_softirq_irqoff(RCU_SOFTIRQ);
                } else {
                        // Enabling BH or preempt does reschedule, so...
-                       // Also if no expediting or NO_HZ_FULL, slow is OK.
+                       // Also if no expediting, slow is OK.
+                       // Plus nohz_full CPUs eventually get tick enabled.
                        set_tsk_need_resched(current);
                        set_preempt_need_resched();
                        if (IS_ENABLED(CONFIG_IRQ_WORK) && irqs_were_disabled &&
@@ -640,7 +627,6 @@ static void rcu_read_unlock_special(struct task_struct *t)
                                irq_work_queue_on(&rdp->defer_qs_iw, rdp->cpu);
                        }
                }
-               t->rcu_read_unlock_special.b.deferred_qs = true;
                local_irq_restore(flags);
                return;
        }
@@ -699,7 +685,7 @@ static void rcu_flavor_sched_clock_irq(int user)
        } else if (rcu_preempt_need_deferred_qs(t)) {
                rcu_preempt_deferred_qs(t); /* Report deferred QS. */
                return;
-       } else if (!rcu_preempt_depth()) {
+       } else if (!WARN_ON_ONCE(rcu_preempt_depth())) {
                rcu_qs(); /* Report immediate QS. */
                return;
        }
@@ -760,8 +746,8 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck)
                pr_info("%s: %d:%d ->qsmask %#lx ->qsmaskinit %#lx ->qsmaskinitnext %#lx\n",
                        __func__, rnp1->grplo, rnp1->grphi, rnp1->qsmask, rnp1->qsmaskinit, rnp1->qsmaskinitnext);
        pr_info("%s: ->gp_tasks %p ->boost_tasks %p ->exp_tasks %p\n",
-               __func__, READ_ONCE(rnp->gp_tasks), rnp->boost_tasks,
-               rnp->exp_tasks);
+               __func__, READ_ONCE(rnp->gp_tasks), data_race(rnp->boost_tasks),
+               READ_ONCE(rnp->exp_tasks));
        pr_info("%s: ->blkd_tasks", __func__);
        i = 0;
        list_for_each(lhp, &rnp->blkd_tasks) {
@@ -854,8 +840,7 @@ void rcu_note_context_switch(bool preempt)
        this_cpu_write(rcu_data.rcu_urgent_qs, false);
        if (unlikely(raw_cpu_read(rcu_data.rcu_need_heavy_qs)))
                rcu_momentary_dyntick_idle();
-       if (!preempt)
-               rcu_tasks_qs(current);
+       rcu_tasks_qs(current, preempt);
 out:
        trace_rcu_utilization(TPS("End context switch"));
 }
@@ -1036,7 +1021,8 @@ static int rcu_boost_kthread(void *arg)
        for (;;) {
                WRITE_ONCE(rnp->boost_kthread_status, RCU_KTHREAD_WAITING);
                trace_rcu_utilization(TPS("End boost kthread@rcu_wait"));
-               rcu_wait(rnp->boost_tasks || rnp->exp_tasks);
+               rcu_wait(READ_ONCE(rnp->boost_tasks) ||
+                        READ_ONCE(rnp->exp_tasks));
                trace_rcu_utilization(TPS("Start boost kthread@rcu_wait"));
                WRITE_ONCE(rnp->boost_kthread_status, RCU_KTHREAD_RUNNING);
                more2boost = rcu_boost(rnp);
@@ -1079,9 +1065,9 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
            (rnp->gp_tasks != NULL &&
             rnp->boost_tasks == NULL &&
             rnp->qsmask == 0 &&
-            (ULONG_CMP_GE(jiffies, rnp->boost_time) || rcu_state.cbovld))) {
+            (!time_after(rnp->boost_time, jiffies) || rcu_state.cbovld))) {
                if (rnp->exp_tasks == NULL)
-                       rnp->boost_tasks = rnp->gp_tasks;
+                       WRITE_ONCE(rnp->boost_tasks, rnp->gp_tasks);
                raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
                rcu_wake_cond(rnp->boost_kthread_task,
                              READ_ONCE(rnp->boost_kthread_status));
@@ -2536,7 +2522,7 @@ static bool rcu_nohz_full_cpu(void)
 #ifdef CONFIG_NO_HZ_FULL
        if (tick_nohz_full_cpu(smp_processor_id()) &&
            (!rcu_gp_in_progress() ||
-            ULONG_CMP_LT(jiffies, READ_ONCE(rcu_state.gp_start) + HZ)))
+            time_before(jiffies, READ_ONCE(rcu_state.gp_start) + HZ)))
                return true;
 #endif /* #ifdef CONFIG_NO_HZ_FULL */
        return false;
@@ -2553,7 +2539,7 @@ static void rcu_bind_gp_kthread(void)
 }
 
 /* Record the current task on dyntick-idle entry. */
-static void rcu_dynticks_task_enter(void)
+static void noinstr rcu_dynticks_task_enter(void)
 {
 #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL)
        WRITE_ONCE(current->rcu_tasks_idle_cpu, smp_processor_id());
@@ -2561,9 +2547,27 @@ static void rcu_dynticks_task_enter(void)
 }
 
 /* Record no current task on dyntick-idle exit. */
-static void rcu_dynticks_task_exit(void)
+static void noinstr rcu_dynticks_task_exit(void)
 {
 #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL)
        WRITE_ONCE(current->rcu_tasks_idle_cpu, -1);
 #endif /* #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) */
 }
+
+/* Turn on heavyweight RCU tasks trace readers on idle/user entry. */
+static void rcu_dynticks_task_trace_enter(void)
+{
+#ifdef CONFIG_TASKS_RCU_TRACE
+       if (IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB))
+               current->trc_reader_special.b.need_mb = true;
+#endif /* #ifdef CONFIG_TASKS_RCU_TRACE */
+}
+
+/* Turn off heavyweight RCU tasks trace readers on idle/user exit. */
+static void rcu_dynticks_task_trace_exit(void)
+{
+#ifdef CONFIG_TASKS_RCU_TRACE
+       if (IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB))
+               current->trc_reader_special.b.need_mb = false;
+#endif /* #ifdef CONFIG_TASKS_RCU_TRACE */
+}
index 119ed6afd20feb84e940a0ea01a79ab36d43e423..ae76bd329582929ba2ba988f49f39508ace881bc 100644 (file)
 int sysctl_panic_on_rcu_stall __read_mostly;
 
 #ifdef CONFIG_PROVE_RCU
-#define RCU_STALL_DELAY_DELTA         (5 * HZ)
+#define RCU_STALL_DELAY_DELTA          (5 * HZ)
 #else
-#define RCU_STALL_DELAY_DELTA         0
+#define RCU_STALL_DELAY_DELTA          0
 #endif
+#define RCU_STALL_MIGHT_DIV            8
+#define RCU_STALL_MIGHT_MIN            (2 * HZ)
 
 /* Limit-check stall timeouts specified at boottime and runtime. */
 int rcu_jiffies_till_stall_check(void)
@@ -40,6 +42,36 @@ int rcu_jiffies_till_stall_check(void)
 }
 EXPORT_SYMBOL_GPL(rcu_jiffies_till_stall_check);
 
+/**
+ * rcu_gp_might_be_stalled - Is it likely that the grace period is stalled?
+ *
+ * Returns @true if the current grace period is sufficiently old that
+ * it is reasonable to assume that it might be stalled.  This can be
+ * useful when deciding whether to allocate memory to enable RCU-mediated
+ * freeing on the one hand or just invoking synchronize_rcu() on the other.
+ * The latter is preferable when the grace period is stalled.
+ *
+ * Note that sampling of the .gp_start and .gp_seq fields must be done
+ * carefully to avoid false positives at the beginnings and ends of
+ * grace periods.
+ */
+bool rcu_gp_might_be_stalled(void)
+{
+       unsigned long d = rcu_jiffies_till_stall_check() / RCU_STALL_MIGHT_DIV;
+       unsigned long j = jiffies;
+
+       if (d < RCU_STALL_MIGHT_MIN)
+               d = RCU_STALL_MIGHT_MIN;
+       smp_mb(); // jiffies before .gp_seq to avoid false positives.
+       if (!rcu_gp_in_progress())
+               return false;
+       // Long delays at this point avoids false positive, but a delay
+       // of ULONG_MAX/4 jiffies voids your no-false-positive warranty.
+       smp_mb(); // .gp_seq before second .gp_start
+       // And ditto here.
+       return !time_before(j, READ_ONCE(rcu_state.gp_start) + d);
+}
+
 /* Don't do RCU CPU stall warnings during long sysrq printouts. */
 void rcu_sysrq_start(void)
 {
@@ -104,8 +136,8 @@ static void record_gp_stall_check_time(void)
 
        WRITE_ONCE(rcu_state.gp_start, j);
        j1 = rcu_jiffies_till_stall_check();
-       /* Record ->gp_start before ->jiffies_stall. */
-       smp_store_release(&rcu_state.jiffies_stall, j + j1); /* ^^^ */
+       smp_mb(); // ->gp_start before ->jiffies_stall and caller's ->gp_seq.
+       WRITE_ONCE(rcu_state.jiffies_stall, j + j1);
        rcu_state.jiffies_resched = j + j1 / 2;
        rcu_state.n_force_qs_gpstart = READ_ONCE(rcu_state.n_force_qs);
 }
@@ -192,14 +224,40 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
        raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
 }
 
+// Communicate task state back to the RCU CPU stall warning request.
+struct rcu_stall_chk_rdr {
+       int nesting;
+       union rcu_special rs;
+       bool on_blkd_list;
+};
+
+/*
+ * Report out the state of a not-running task that is stalling the
+ * current RCU grace period.
+ */
+static bool check_slow_task(struct task_struct *t, void *arg)
+{
+       struct rcu_node *rnp;
+       struct rcu_stall_chk_rdr *rscrp = arg;
+
+       if (task_curr(t))
+               return false; // It is running, so decline to inspect it.
+       rscrp->nesting = t->rcu_read_lock_nesting;
+       rscrp->rs = t->rcu_read_unlock_special;
+       rnp = t->rcu_blocked_node;
+       rscrp->on_blkd_list = !list_empty(&t->rcu_node_entry);
+       return true;
+}
+
 /*
  * Scan the current list of tasks blocked within RCU read-side critical
  * sections, printing out the tid of each.
  */
 static int rcu_print_task_stall(struct rcu_node *rnp)
 {
-       struct task_struct *t;
        int ndetected = 0;
+       struct rcu_stall_chk_rdr rscr;
+       struct task_struct *t;
 
        if (!rcu_preempt_blocked_readers_cgp(rnp))
                return 0;
@@ -208,7 +266,15 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
        t = list_entry(rnp->gp_tasks->prev,
                       struct task_struct, rcu_node_entry);
        list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
-               pr_cont(" P%d", t->pid);
+               if (!try_invoke_on_locked_down_task(t, check_slow_task, &rscr))
+                       pr_cont(" P%d", t->pid);
+               else
+                       pr_cont(" P%d/%d:%c%c%c%c",
+                               t->pid, rscr.nesting,
+                               ".b"[rscr.rs.b.blocked],
+                               ".q"[rscr.rs.b.need_qs],
+                               ".e"[rscr.rs.b.exp_hint],
+                               ".l"[rscr.on_blkd_list]);
                ndetected++;
        }
        pr_cont("\n");
@@ -299,6 +365,16 @@ static const char *gp_state_getname(short gs)
        return gp_state_names[gs];
 }
 
+/* Is the RCU grace-period kthread being starved of CPU time? */
+static bool rcu_is_gp_kthread_starving(unsigned long *jp)
+{
+       unsigned long j = jiffies - READ_ONCE(rcu_state.gp_activity);
+
+       if (jp)
+               *jp = j;
+       return j > 2 * HZ;
+}
+
 /*
  * Print out diagnostic information for the specified stalled CPU.
  *
@@ -313,6 +389,7 @@ static const char *gp_state_getname(short gs)
 static void print_cpu_stall_info(int cpu)
 {
        unsigned long delta;
+       bool falsepositive;
        char fast_no_hz[72];
        struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
        char *ticks_title;
@@ -333,7 +410,9 @@ static void print_cpu_stall_info(int cpu)
        }
        print_cpu_stall_fast_no_hz(fast_no_hz, cpu);
        delta = rcu_seq_ctr(rdp->mynode->gp_seq - rdp->rcu_iw_gp_seq);
-       pr_err("\t%d-%c%c%c%c: (%lu %s) idle=%03x/%ld/%#lx softirq=%u/%u fqs=%ld %s\n",
+       falsepositive = rcu_is_gp_kthread_starving(NULL) &&
+                       rcu_dynticks_in_eqs(rcu_dynticks_snap(rdp));
+       pr_err("\t%d-%c%c%c%c: (%lu %s) idle=%03x/%ld/%#lx softirq=%u/%u fqs=%ld %s%s\n",
               cpu,
               "O."[!!cpu_online(cpu)],
               "o."[!!(rdp->grpmask & rdp->mynode->qsmaskinit)],
@@ -345,8 +424,9 @@ static void print_cpu_stall_info(int cpu)
               rcu_dynticks_snap(rdp) & 0xfff,
               rdp->dynticks_nesting, rdp->dynticks_nmi_nesting,
               rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu),
-              READ_ONCE(rcu_state.n_force_qs) - rcu_state.n_force_qs_gpstart,
-              fast_no_hz);
+              data_race(rcu_state.n_force_qs) - rcu_state.n_force_qs_gpstart,
+              fast_no_hz,
+              falsepositive ? " (false positive?)" : "");
 }
 
 /* Complain about starvation of grace-period kthread.  */
@@ -355,15 +435,15 @@ static void rcu_check_gp_kthread_starvation(void)
        struct task_struct *gpk = rcu_state.gp_kthread;
        unsigned long j;
 
-       j = jiffies - READ_ONCE(rcu_state.gp_activity);
-       if (j > 2 * HZ) {
+       if (rcu_is_gp_kthread_starving(&j)) {
                pr_err("%s kthread starved for %ld jiffies! g%ld f%#x %s(%d) ->state=%#lx ->cpu=%d\n",
                       rcu_state.name, j,
                       (long)rcu_seq_current(&rcu_state.gp_seq),
-                      READ_ONCE(rcu_state.gp_flags),
+                      data_race(rcu_state.gp_flags),
                       gp_state_getname(rcu_state.gp_state), rcu_state.gp_state,
                       gpk ? gpk->state : ~0, gpk ? task_cpu(gpk) : -1);
                if (gpk) {
+                       pr_err("\tUnless %s kthread gets sufficient CPU time, OOM is now expected behavior.\n", rcu_state.name);
                        pr_err("RCU grace-period kthread stack dump:\n");
                        sched_show_task(gpk);
                        wake_up_process(gpk);
@@ -371,7 +451,7 @@ static void rcu_check_gp_kthread_starvation(void)
        }
 }
 
-static void print_other_cpu_stall(unsigned long gp_seq)
+static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps)
 {
        int cpu;
        unsigned long flags;
@@ -408,7 +488,7 @@ static void print_other_cpu_stall(unsigned long gp_seq)
        for_each_possible_cpu(cpu)
                totqlen += rcu_get_n_cbs_cpu(cpu);
        pr_cont("\t(detected by %d, t=%ld jiffies, g=%ld, q=%lu)\n",
-              smp_processor_id(), (long)(jiffies - rcu_state.gp_start),
+              smp_processor_id(), (long)(jiffies - gps),
               (long)rcu_seq_current(&rcu_state.gp_seq), totqlen);
        if (ndetected) {
                rcu_dump_cpu_stacks();
@@ -421,13 +501,11 @@ static void print_other_cpu_stall(unsigned long gp_seq)
                        pr_err("INFO: Stall ended before state dump start\n");
                } else {
                        j = jiffies;
-                       gpa = READ_ONCE(rcu_state.gp_activity);
+                       gpa = data_race(rcu_state.gp_activity);
                        pr_err("All QSes seen, last %s kthread activity %ld (%ld-%ld), jiffies_till_next_fqs=%ld, root ->qsmask %#lx\n",
                               rcu_state.name, j - gpa, j, gpa,
-                              READ_ONCE(jiffies_till_next_fqs),
+                              data_race(jiffies_till_next_fqs),
                               rcu_get_root()->qsmask);
-                       /* In this case, the current CPU might be at fault. */
-                       sched_show_task(current);
                }
        }
        /* Rewrite if needed in case of slow consoles. */
@@ -442,7 +520,7 @@ static void print_other_cpu_stall(unsigned long gp_seq)
        rcu_force_quiescent_state();  /* Kick them all. */
 }
 
-static void print_cpu_stall(void)
+static void print_cpu_stall(unsigned long gps)
 {
        int cpu;
        unsigned long flags;
@@ -467,7 +545,7 @@ static void print_cpu_stall(void)
        for_each_possible_cpu(cpu)
                totqlen += rcu_get_n_cbs_cpu(cpu);
        pr_cont("\t(t=%lu jiffies g=%ld q=%lu)\n",
-               jiffies - rcu_state.gp_start,
+               jiffies - gps,
                (long)rcu_seq_current(&rcu_state.gp_seq), totqlen);
 
        rcu_check_gp_kthread_starvation();
@@ -546,7 +624,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
            cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) {
 
                /* We haven't checked in, so go dump stack. */
-               print_cpu_stall();
+               print_cpu_stall(gps);
                if (rcu_cpu_stall_ftrace_dump)
                        rcu_ftrace_dump(DUMP_ALL);
 
@@ -555,7 +633,7 @@ static void check_cpu_stall(struct rcu_data *rdp)
                   cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) {
 
                /* They had a few time units to dump stack, so complain. */
-               print_other_cpu_stall(gs2);
+               print_other_cpu_stall(gs2, gps);
                if (rcu_cpu_stall_ftrace_dump)
                        rcu_ftrace_dump(DUMP_ALL);
        }
@@ -581,23 +659,23 @@ void show_rcu_gp_kthreads(void)
        struct task_struct *t = READ_ONCE(rcu_state.gp_kthread);
 
        j = jiffies;
-       ja = j - READ_ONCE(rcu_state.gp_activity);
-       jr = j - READ_ONCE(rcu_state.gp_req_activity);
-       jw = j - READ_ONCE(rcu_state.gp_wake_time);
+       ja = j - data_race(rcu_state.gp_activity);
+       jr = j - data_race(rcu_state.gp_req_activity);
+       jw = j - data_race(rcu_state.gp_wake_time);
        pr_info("%s: wait state: %s(%d) ->state: %#lx delta ->gp_activity %lu ->gp_req_activity %lu ->gp_wake_time %lu ->gp_wake_seq %ld ->gp_seq %ld ->gp_seq_needed %ld ->gp_flags %#x\n",
                rcu_state.name, gp_state_getname(rcu_state.gp_state),
                rcu_state.gp_state, t ? t->state : 0x1ffffL,
-               ja, jr, jw, (long)READ_ONCE(rcu_state.gp_wake_seq),
-               (long)READ_ONCE(rcu_state.gp_seq),
-               (long)READ_ONCE(rcu_get_root()->gp_seq_needed),
-               READ_ONCE(rcu_state.gp_flags));
+               ja, jr, jw, (long)data_race(rcu_state.gp_wake_seq),
+               (long)data_race(rcu_state.gp_seq),
+               (long)data_race(rcu_get_root()->gp_seq_needed),
+               data_race(rcu_state.gp_flags));
        rcu_for_each_node_breadth_first(rnp) {
                if (ULONG_CMP_GE(READ_ONCE(rcu_state.gp_seq),
                                 READ_ONCE(rnp->gp_seq_needed)))
                        continue;
                pr_info("\trcu_node %d:%d ->gp_seq %ld ->gp_seq_needed %ld\n",
-                       rnp->grplo, rnp->grphi, (long)READ_ONCE(rnp->gp_seq),
-                       (long)READ_ONCE(rnp->gp_seq_needed));
+                       rnp->grplo, rnp->grphi, (long)data_race(rnp->gp_seq),
+                       (long)data_race(rnp->gp_seq_needed));
                if (!rcu_is_leaf_node(rnp))
                        continue;
                for_each_leaf_node_possible_cpu(rnp, cpu) {
@@ -607,7 +685,7 @@ void show_rcu_gp_kthreads(void)
                                         READ_ONCE(rdp->gp_seq_needed)))
                                continue;
                        pr_info("\tcpu %d ->gp_seq_needed %ld\n",
-                               cpu, (long)READ_ONCE(rdp->gp_seq_needed));
+                               cpu, (long)data_race(rdp->gp_seq_needed));
                }
        }
        for_each_possible_cpu(cpu) {
@@ -615,7 +693,7 @@ void show_rcu_gp_kthreads(void)
                if (rcu_segcblist_is_offloaded(&rdp->cblist))
                        show_rcu_nocb_state(rdp);
        }
-       /* sched_show_task(rcu_state.gp_kthread); */
+       show_rcu_tasks_gp_kthreads();
 }
 EXPORT_SYMBOL_GPL(show_rcu_gp_kthreads);
 
index 28a8bdc5072f1f3ab676f5a84bc743924b091cb4..84843adfd9394e2e97ae86c67dbe63fa24c972ce 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/sched/isolation.h>
 #include <linux/kprobes.h>
 #include <linux/slab.h>
+#include <linux/irq_work.h>
 
 #define CREATE_TRACE_POINTS
 
 #endif
 #define MODULE_PARAM_PREFIX "rcupdate."
 
+#ifndef data_race
+#define data_race(expr)                                                        \
+       ({                                                              \
+               expr;                                                   \
+       })
+#endif
+#ifndef ASSERT_EXCLUSIVE_WRITER
+#define ASSERT_EXCLUSIVE_WRITER(var) do { } while (0)
+#endif
+#ifndef ASSERT_EXCLUSIVE_ACCESS
+#define ASSERT_EXCLUSIVE_ACCESS(var) do { } while (0)
+#endif
+
 #ifndef CONFIG_TINY_RCU
 module_param(rcu_expedited, int, 0);
 module_param(rcu_normal, int, 0);
@@ -63,12 +77,12 @@ module_param(rcu_normal_after_boot, int, 0);
  * rcu_read_lock_held_common() - might we be in RCU-sched read-side critical section?
  * @ret:       Best guess answer if lockdep cannot be relied on
  *
- * Returns true if lockdep must be ignored, in which case *ret contains
+ * Returns true if lockdep must be ignored, in which case ``*ret`` contains
  * the best guess described below.  Otherwise returns false, in which
- * case *ret tells the caller nothing and the caller should instead
+ * case ``*ret`` tells the caller nothing and the caller should instead
  * consult lockdep.
  *
- * If CONFIG_DEBUG_LOCK_ALLOC is selected, set *ret to nonzero iff in an
+ * If CONFIG_DEBUG_LOCK_ALLOC is selected, set ``*ret`` to nonzero iff in an
  * RCU-sched read-side critical section.  In absence of
  * CONFIG_DEBUG_LOCK_ALLOC, this assumes we are in an RCU-sched read-side
  * critical section unless it can prove otherwise.  Note that disabling
@@ -82,7 +96,7 @@ module_param(rcu_normal_after_boot, int, 0);
  *
  * Note that if the CPU is in the idle loop from an RCU point of view (ie:
  * that we are in the section between rcu_idle_enter() and rcu_idle_exit())
- * then rcu_read_lock_held() sets *ret to false even if the CPU did an
+ * then rcu_read_lock_held() sets ``*ret`` to false even if the CPU did an
  * rcu_read_lock().  The reason for this is that RCU ignores CPUs that are
  * in such a section, considering these as in extended quiescent state,
  * so such a CPU is effectively never in an RCU read-side critical section
@@ -98,15 +112,15 @@ module_param(rcu_normal_after_boot, int, 0);
 static bool rcu_read_lock_held_common(bool *ret)
 {
        if (!debug_lockdep_rcu_enabled()) {
-               *ret = 1;
+               *ret = true;
                return true;
        }
        if (!rcu_is_watching()) {
-               *ret = 0;
+               *ret = false;
                return true;
        }
        if (!rcu_lockdep_current_cpu_online()) {
-               *ret = 0;
+               *ret = false;
                return true;
        }
        return false;
@@ -270,13 +284,12 @@ struct lockdep_map rcu_callback_map =
        STATIC_LOCKDEP_MAP_INIT("rcu_callback", &rcu_callback_key);
 EXPORT_SYMBOL_GPL(rcu_callback_map);
 
-int notrace debug_lockdep_rcu_enabled(void)
+noinstr int notrace debug_lockdep_rcu_enabled(void)
 {
        return rcu_scheduler_active != RCU_SCHEDULER_INACTIVE && debug_locks &&
               current->lockdep_recursion == 0;
 }
 EXPORT_SYMBOL_GPL(debug_lockdep_rcu_enabled);
-NOKPROBE_SYMBOL(debug_lockdep_rcu_enabled);
 
 /**
  * rcu_read_lock_held() - might we be in RCU read-side critical section?
@@ -501,370 +514,6 @@ int rcu_cpu_stall_suppress_at_boot __read_mostly; // !0 = suppress boot stalls.
 EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress_at_boot);
 module_param(rcu_cpu_stall_suppress_at_boot, int, 0444);
 
-#ifdef CONFIG_TASKS_RCU
-
-/*
- * Simple variant of RCU whose quiescent states are voluntary context
- * switch, cond_resched_rcu_qs(), user-space execution, and idle.
- * As such, grace periods can take one good long time.  There are no
- * read-side primitives similar to rcu_read_lock() and rcu_read_unlock()
- * because this implementation is intended to get the system into a safe
- * state for some of the manipulations involved in tracing and the like.
- * Finally, this implementation does not support high call_rcu_tasks()
- * rates from multiple CPUs.  If this is required, per-CPU callback lists
- * will be needed.
- */
-
-/* Global list of callbacks and associated lock. */
-static struct rcu_head *rcu_tasks_cbs_head;
-static struct rcu_head **rcu_tasks_cbs_tail = &rcu_tasks_cbs_head;
-static DECLARE_WAIT_QUEUE_HEAD(rcu_tasks_cbs_wq);
-static DEFINE_RAW_SPINLOCK(rcu_tasks_cbs_lock);
-
-/* Track exiting tasks in order to allow them to be waited for. */
-DEFINE_STATIC_SRCU(tasks_rcu_exit_srcu);
-
-/* Control stall timeouts.  Disable with <= 0, otherwise jiffies till stall. */
-#define RCU_TASK_STALL_TIMEOUT (HZ * 60 * 10)
-static int rcu_task_stall_timeout __read_mostly = RCU_TASK_STALL_TIMEOUT;
-module_param(rcu_task_stall_timeout, int, 0644);
-
-static struct task_struct *rcu_tasks_kthread_ptr;
-
-/**
- * call_rcu_tasks() - Queue an RCU for invocation task-based grace period
- * @rhp: structure to be used for queueing the RCU updates.
- * @func: actual callback function to be invoked after the grace period
- *
- * The callback function will be invoked some time after a full grace
- * period elapses, in other words after all currently executing RCU
- * read-side critical sections have completed. call_rcu_tasks() assumes
- * that the read-side critical sections end at a voluntary context
- * switch (not a preemption!), cond_resched_rcu_qs(), entry into idle,
- * or transition to usermode execution.  As such, there are no read-side
- * primitives analogous to rcu_read_lock() and rcu_read_unlock() because
- * this primitive is intended to determine that all tasks have passed
- * through a safe state, not so much for data-strcuture synchronization.
- *
- * See the description of call_rcu() for more detailed information on
- * memory ordering guarantees.
- */
-void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)
-{
-       unsigned long flags;
-       bool needwake;
-
-       rhp->next = NULL;
-       rhp->func = func;
-       raw_spin_lock_irqsave(&rcu_tasks_cbs_lock, flags);
-       needwake = !rcu_tasks_cbs_head;
-       WRITE_ONCE(*rcu_tasks_cbs_tail, rhp);
-       rcu_tasks_cbs_tail = &rhp->next;
-       raw_spin_unlock_irqrestore(&rcu_tasks_cbs_lock, flags);
-       /* We can't create the thread unless interrupts are enabled. */
-       if (needwake && READ_ONCE(rcu_tasks_kthread_ptr))
-               wake_up(&rcu_tasks_cbs_wq);
-}
-EXPORT_SYMBOL_GPL(call_rcu_tasks);
-
-/**
- * synchronize_rcu_tasks - wait until an rcu-tasks grace period has elapsed.
- *
- * Control will return to the caller some time after a full rcu-tasks
- * grace period has elapsed, in other words after all currently
- * executing rcu-tasks read-side critical sections have elapsed.  These
- * read-side critical sections are delimited by calls to schedule(),
- * cond_resched_tasks_rcu_qs(), idle execution, userspace execution, calls
- * to synchronize_rcu_tasks(), and (in theory, anyway) cond_resched().
- *
- * This is a very specialized primitive, intended only for a few uses in
- * tracing and other situations requiring manipulation of function
- * preambles and profiling hooks.  The synchronize_rcu_tasks() function
- * is not (yet) intended for heavy use from multiple CPUs.
- *
- * Note that this guarantee implies further memory-ordering guarantees.
- * On systems with more than one CPU, when synchronize_rcu_tasks() returns,
- * each CPU is guaranteed to have executed a full memory barrier since the
- * end of its last RCU-tasks read-side critical section whose beginning
- * preceded the call to synchronize_rcu_tasks().  In addition, each CPU
- * having an RCU-tasks read-side critical section that extends beyond
- * the return from synchronize_rcu_tasks() is guaranteed to have executed
- * a full memory barrier after the beginning of synchronize_rcu_tasks()
- * and before the beginning of that RCU-tasks read-side critical section.
- * Note that these guarantees include CPUs that are offline, idle, or
- * executing in user mode, as well as CPUs that are executing in the kernel.
- *
- * Furthermore, if CPU A invoked synchronize_rcu_tasks(), which returned
- * to its caller on CPU B, then both CPU A and CPU B are guaranteed
- * to have executed a full memory barrier during the execution of
- * synchronize_rcu_tasks() -- even if CPU A and CPU B are the same CPU
- * (but again only if the system has more than one CPU).
- */
-void synchronize_rcu_tasks(void)
-{
-       /* Complain if the scheduler has not started.  */
-       RCU_LOCKDEP_WARN(rcu_scheduler_active == RCU_SCHEDULER_INACTIVE,
-                        "synchronize_rcu_tasks called too soon");
-
-       /* Wait for the grace period. */
-       wait_rcu_gp(call_rcu_tasks);
-}
-EXPORT_SYMBOL_GPL(synchronize_rcu_tasks);
-
-/**
- * rcu_barrier_tasks - Wait for in-flight call_rcu_tasks() callbacks.
- *
- * Although the current implementation is guaranteed to wait, it is not
- * obligated to, for example, if there are no pending callbacks.
- */
-void rcu_barrier_tasks(void)
-{
-       /* There is only one callback queue, so this is easy.  ;-) */
-       synchronize_rcu_tasks();
-}
-EXPORT_SYMBOL_GPL(rcu_barrier_tasks);
-
-/* See if tasks are still holding out, complain if so. */
-static void check_holdout_task(struct task_struct *t,
-                              bool needreport, bool *firstreport)
-{
-       int cpu;
-
-       if (!READ_ONCE(t->rcu_tasks_holdout) ||
-           t->rcu_tasks_nvcsw != READ_ONCE(t->nvcsw) ||
-           !READ_ONCE(t->on_rq) ||
-           (IS_ENABLED(CONFIG_NO_HZ_FULL) &&
-            !is_idle_task(t) && t->rcu_tasks_idle_cpu >= 0)) {
-               WRITE_ONCE(t->rcu_tasks_holdout, false);
-               list_del_init(&t->rcu_tasks_holdout_list);
-               put_task_struct(t);
-               return;
-       }
-       rcu_request_urgent_qs_task(t);
-       if (!needreport)
-               return;
-       if (*firstreport) {
-               pr_err("INFO: rcu_tasks detected stalls on tasks:\n");
-               *firstreport = false;
-       }
-       cpu = task_cpu(t);
-       pr_alert("%p: %c%c nvcsw: %lu/%lu holdout: %d idle_cpu: %d/%d\n",
-                t, ".I"[is_idle_task(t)],
-                "N."[cpu < 0 || !tick_nohz_full_cpu(cpu)],
-                t->rcu_tasks_nvcsw, t->nvcsw, t->rcu_tasks_holdout,
-                t->rcu_tasks_idle_cpu, cpu);
-       sched_show_task(t);
-}
-
-/* RCU-tasks kthread that detects grace periods and invokes callbacks. */
-static int __noreturn rcu_tasks_kthread(void *arg)
-{
-       unsigned long flags;
-       struct task_struct *g, *t;
-       unsigned long lastreport;
-       struct rcu_head *list;
-       struct rcu_head *next;
-       LIST_HEAD(rcu_tasks_holdouts);
-       int fract;
-
-       /* Run on housekeeping CPUs by default.  Sysadm can move if desired. */
-       housekeeping_affine(current, HK_FLAG_RCU);
-
-       /*
-        * Each pass through the following loop makes one check for
-        * newly arrived callbacks, and, if there are some, waits for
-        * one RCU-tasks grace period and then invokes the callbacks.
-        * This loop is terminated by the system going down.  ;-)
-        */
-       for (;;) {
-
-               /* Pick up any new callbacks. */
-               raw_spin_lock_irqsave(&rcu_tasks_cbs_lock, flags);
-               list = rcu_tasks_cbs_head;
-               rcu_tasks_cbs_head = NULL;
-               rcu_tasks_cbs_tail = &rcu_tasks_cbs_head;
-               raw_spin_unlock_irqrestore(&rcu_tasks_cbs_lock, flags);
-
-               /* If there were none, wait a bit and start over. */
-               if (!list) {
-                       wait_event_interruptible(rcu_tasks_cbs_wq,
-                                                READ_ONCE(rcu_tasks_cbs_head));
-                       if (!rcu_tasks_cbs_head) {
-                               WARN_ON(signal_pending(current));
-                               schedule_timeout_interruptible(HZ/10);
-                       }
-                       continue;
-               }
-
-               /*
-                * Wait for all pre-existing t->on_rq and t->nvcsw
-                * transitions to complete.  Invoking synchronize_rcu()
-                * suffices because all these transitions occur with
-                * interrupts disabled.  Without this synchronize_rcu(),
-                * a read-side critical section that started before the
-                * grace period might be incorrectly seen as having started
-                * after the grace period.
-                *
-                * This synchronize_rcu() also dispenses with the
-                * need for a memory barrier on the first store to
-                * ->rcu_tasks_holdout, as it forces the store to happen
-                * after the beginning of the grace period.
-                */
-               synchronize_rcu();
-
-               /*
-                * There were callbacks, so we need to wait for an
-                * RCU-tasks grace period.  Start off by scanning
-                * the task list for tasks that are not already
-                * voluntarily blocked.  Mark these tasks and make
-                * a list of them in rcu_tasks_holdouts.
-                */
-               rcu_read_lock();
-               for_each_process_thread(g, t) {
-                       if (t != current && READ_ONCE(t->on_rq) &&
-                           !is_idle_task(t)) {
-                               get_task_struct(t);
-                               t->rcu_tasks_nvcsw = READ_ONCE(t->nvcsw);
-                               WRITE_ONCE(t->rcu_tasks_holdout, true);
-                               list_add(&t->rcu_tasks_holdout_list,
-                                        &rcu_tasks_holdouts);
-                       }
-               }
-               rcu_read_unlock();
-
-               /*
-                * Wait for tasks that are in the process of exiting.
-                * This does only part of the job, ensuring that all
-                * tasks that were previously exiting reach the point
-                * where they have disabled preemption, allowing the
-                * later synchronize_rcu() to finish the job.
-                */
-               synchronize_srcu(&tasks_rcu_exit_srcu);
-
-               /*
-                * Each pass through the following loop scans the list
-                * of holdout tasks, removing any that are no longer
-                * holdouts.  When the list is empty, we are done.
-                */
-               lastreport = jiffies;
-
-               /* Start off with HZ/10 wait and slowly back off to 1 HZ wait*/
-               fract = 10;
-
-               for (;;) {
-                       bool firstreport;
-                       bool needreport;
-                       int rtst;
-                       struct task_struct *t1;
-
-                       if (list_empty(&rcu_tasks_holdouts))
-                               break;
-
-                       /* Slowly back off waiting for holdouts */
-                       schedule_timeout_interruptible(HZ/fract);
-
-                       if (fract > 1)
-                               fract--;
-
-                       rtst = READ_ONCE(rcu_task_stall_timeout);
-                       needreport = rtst > 0 &&
-                                    time_after(jiffies, lastreport + rtst);
-                       if (needreport)
-                               lastreport = jiffies;
-                       firstreport = true;
-                       WARN_ON(signal_pending(current));
-                       list_for_each_entry_safe(t, t1, &rcu_tasks_holdouts,
-                                               rcu_tasks_holdout_list) {
-                               check_holdout_task(t, needreport, &firstreport);
-                               cond_resched();
-                       }
-               }
-
-               /*
-                * Because ->on_rq and ->nvcsw are not guaranteed
-                * to have a full memory barriers prior to them in the
-                * schedule() path, memory reordering on other CPUs could
-                * cause their RCU-tasks read-side critical sections to
-                * extend past the end of the grace period.  However,
-                * because these ->nvcsw updates are carried out with
-                * interrupts disabled, we can use synchronize_rcu()
-                * to force the needed ordering on all such CPUs.
-                *
-                * This synchronize_rcu() also confines all
-                * ->rcu_tasks_holdout accesses to be within the grace
-                * period, avoiding the need for memory barriers for
-                * ->rcu_tasks_holdout accesses.
-                *
-                * In addition, this synchronize_rcu() waits for exiting
-                * tasks to complete their final preempt_disable() region
-                * of execution, cleaning up after the synchronize_srcu()
-                * above.
-                */
-               synchronize_rcu();
-
-               /* Invoke the callbacks. */
-               while (list) {
-                       next = list->next;
-                       local_bh_disable();
-                       list->func(list);
-                       local_bh_enable();
-                       list = next;
-                       cond_resched();
-               }
-               /* Paranoid sleep to keep this from entering a tight loop */
-               schedule_timeout_uninterruptible(HZ/10);
-       }
-}
-
-/* Spawn rcu_tasks_kthread() at core_initcall() time. */
-static int __init rcu_spawn_tasks_kthread(void)
-{
-       struct task_struct *t;
-
-       t = kthread_run(rcu_tasks_kthread, NULL, "rcu_tasks_kthread");
-       if (WARN_ONCE(IS_ERR(t), "%s: Could not start Tasks-RCU grace-period kthread, OOM is now expected behavior\n", __func__))
-               return 0;
-       smp_mb(); /* Ensure others see full kthread. */
-       WRITE_ONCE(rcu_tasks_kthread_ptr, t);
-       return 0;
-}
-core_initcall(rcu_spawn_tasks_kthread);
-
-/* Do the srcu_read_lock() for the above synchronize_srcu().  */
-void exit_tasks_rcu_start(void) __acquires(&tasks_rcu_exit_srcu)
-{
-       preempt_disable();
-       current->rcu_tasks_idx = __srcu_read_lock(&tasks_rcu_exit_srcu);
-       preempt_enable();
-}
-
-/* Do the srcu_read_unlock() for the above synchronize_srcu().  */
-void exit_tasks_rcu_finish(void) __releases(&tasks_rcu_exit_srcu)
-{
-       preempt_disable();
-       __srcu_read_unlock(&tasks_rcu_exit_srcu, current->rcu_tasks_idx);
-       preempt_enable();
-}
-
-#endif /* #ifdef CONFIG_TASKS_RCU */
-
-#ifndef CONFIG_TINY_RCU
-
-/*
- * Print any non-default Tasks RCU settings.
- */
-static void __init rcu_tasks_bootup_oddness(void)
-{
-#ifdef CONFIG_TASKS_RCU
-       if (rcu_task_stall_timeout != RCU_TASK_STALL_TIMEOUT)
-               pr_info("\tTasks-RCU CPU stall warnings timeout set to %d (rcu_task_stall_timeout).\n", rcu_task_stall_timeout);
-       else
-               pr_info("\tTasks RCU enabled.\n");
-#endif /* #ifdef CONFIG_TASKS_RCU */
-}
-
-#endif /* #ifndef CONFIG_TINY_RCU */
-
 #ifdef CONFIG_PROVE_RCU
 
 /*
@@ -935,6 +584,8 @@ late_initcall(rcu_verify_early_boot_tests);
 void rcu_early_boot_tests(void) {}
 #endif /* CONFIG_PROVE_RCU */
 
+#include "tasks.h"
+
 #ifndef CONFIG_TINY_RCU
 
 /*
index c4d472b7f1b42f4194da4daed0ab7da64e4c7b91..491f1347bf4360d43dfdad141817c53d03dd400a 100644 (file)
@@ -250,7 +250,7 @@ void kernel_restart(char *cmd)
                pr_emerg("Restarting system\n");
        else
                pr_emerg("Restarting system with command '%s'\n", cmd);
-       kmsg_dump(KMSG_DUMP_RESTART);
+       kmsg_dump(KMSG_DUMP_SHUTDOWN);
        machine_restart(cmd);
 }
 EXPORT_SYMBOL_GPL(kernel_restart);
@@ -274,7 +274,7 @@ void kernel_halt(void)
        migrate_to_reboot_cpu();
        syscore_shutdown();
        pr_emerg("System halted\n");
-       kmsg_dump(KMSG_DUMP_HALT);
+       kmsg_dump(KMSG_DUMP_SHUTDOWN);
        machine_halt();
 }
 EXPORT_SYMBOL_GPL(kernel_halt);
@@ -292,7 +292,7 @@ void kernel_power_off(void)
        migrate_to_reboot_cpu();
        syscore_shutdown();
        pr_emerg("Power down\n");
-       kmsg_dump(KMSG_DUMP_POWEROFF);
+       kmsg_dump(KMSG_DUMP_SHUTDOWN);
        machine_power_off();
 }
 EXPORT_SYMBOL_GPL(kernel_power_off);
index ade14fb7ce2e60cc676f7807daa0302ce24e7b79..d0c9c287680af0fc554e0007a0107d984d9ea860 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Public API and common code for kernel->userspace relay file support.
  *
- * See Documentation/filesystems/relay.txt for an overview.
+ * See Documentation/filesystems/relay.rst for an overview.
  *
  * Copyright (C) 2002-2005 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
  * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
index 9a2fbf98fd6fa2ea3c420d02ac4cb20d2ba73958..0ae29fd57817d83525e4d2bb6bbcfa5d7e9a684b 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/nospec.h>
 
 #include <linux/kcov.h>
+#include <linux/scs.h>
 
 #include <asm/switch_to.h>
 #include <asm/tlb.h>
@@ -2561,6 +2562,8 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
         *
         * Pairs with the LOCK+smp_mb__after_spinlock() on rq->lock in
         * __schedule().  See the comment for smp_mb__after_spinlock().
+        *
+        * A similar smb_rmb() lives in try_invoke_on_locked_down_task().
         */
        smp_rmb();
        if (p->on_rq && ttwu_remote(p, wake_flags))
@@ -2634,6 +2637,52 @@ out:
        return success;
 }
 
+/**
+ * try_invoke_on_locked_down_task - Invoke a function on task in fixed state
+ * @p: Process for which the function is to be invoked.
+ * @func: Function to invoke.
+ * @arg: Argument to function.
+ *
+ * If the specified task can be quickly locked into a definite state
+ * (either sleeping or on a given runqueue), arrange to keep it in that
+ * state while invoking @func(@arg).  This function can use ->on_rq and
+ * task_curr() to work out what the state is, if required.  Given that
+ * @func can be invoked with a runqueue lock held, it had better be quite
+ * lightweight.
+ *
+ * Returns:
+ *     @false if the task slipped out from under the locks.
+ *     @true if the task was locked onto a runqueue or is sleeping.
+ *             However, @func can override this by returning @false.
+ */
+bool try_invoke_on_locked_down_task(struct task_struct *p, bool (*func)(struct task_struct *t, void *arg), void *arg)
+{
+       bool ret = false;
+       struct rq_flags rf;
+       struct rq *rq;
+
+       lockdep_assert_irqs_enabled();
+       raw_spin_lock_irq(&p->pi_lock);
+       if (p->on_rq) {
+               rq = __task_rq_lock(p, &rf);
+               if (task_rq(p) == rq)
+                       ret = func(p, arg);
+               rq_unlock(rq, &rf);
+       } else {
+               switch (p->state) {
+               case TASK_RUNNING:
+               case TASK_WAKING:
+                       break;
+               default:
+                       smp_rmb(); // See smp_rmb() comment in try_to_wake_up().
+                       if (!p->on_rq)
+                               ret = func(p, arg);
+               }
+       }
+       raw_spin_unlock_irq(&p->pi_lock);
+       return ret;
+}
+
 /**
  * wake_up_process - Wake up a specific process
  * @p: The process to be woken up.
@@ -3877,6 +3926,9 @@ static inline void schedule_debug(struct task_struct *prev, bool preempt)
 #ifdef CONFIG_SCHED_STACK_END_CHECK
        if (task_stack_end_corrupted(prev))
                panic("corrupted stack end detected inside scheduler\n");
+
+       if (task_scs_end_corrupted(prev))
+               panic("corrupted shadow stack detected inside scheduler\n");
 #endif
 
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
@@ -6040,6 +6092,7 @@ void init_idle(struct task_struct *idle, int cpu)
        idle->se.exec_start = sched_clock();
        idle->flags |= PF_IDLE;
 
+       scs_task_reset(idle);
        kasan_unpoison_task_stack(idle);
 
 #ifdef CONFIG_SMP
index 538ba5d94e99cc322b1c993a808cec1c66fa7caf..da3e5b54715b6753eaac360d7562f3deedb26210 100644 (file)
@@ -2908,7 +2908,7 @@ static void task_tick_numa(struct rq *rq, struct task_struct *curr)
        /*
         * We don't care about NUMA placement if we don't have memory.
         */
-       if (!curr->mm || (curr->flags & PF_EXITING) || work->next != work)
+       if ((curr->flags & (PF_EXITING | PF_KTHREAD)) || work->next != work)
                return;
 
        /*
diff --git a/kernel/scs.c b/kernel/scs.c
new file mode 100644 (file)
index 0000000..222a7a9
--- /dev/null
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Shadow Call Stack support.
+ *
+ * Copyright (C) 2019 Google LLC
+ */
+
+#include <linux/kasan.h>
+#include <linux/mm.h>
+#include <linux/scs.h>
+#include <linux/slab.h>
+#include <linux/vmstat.h>
+
+static struct kmem_cache *scs_cache;
+
+static void __scs_account(void *s, int account)
+{
+       struct page *scs_page = virt_to_page(s);
+
+       mod_zone_page_state(page_zone(scs_page), NR_KERNEL_SCS_KB,
+                           account * (SCS_SIZE / SZ_1K));
+}
+
+static void *scs_alloc(int node)
+{
+       void *s = kmem_cache_alloc_node(scs_cache, GFP_SCS, node);
+
+       if (!s)
+               return NULL;
+
+       *__scs_magic(s) = SCS_END_MAGIC;
+
+       /*
+        * Poison the allocation to catch unintentional accesses to
+        * the shadow stack when KASAN is enabled.
+        */
+       kasan_poison_object_data(scs_cache, s);
+       __scs_account(s, 1);
+       return s;
+}
+
+static void scs_free(void *s)
+{
+       __scs_account(s, -1);
+       kasan_unpoison_object_data(scs_cache, s);
+       kmem_cache_free(scs_cache, s);
+}
+
+void __init scs_init(void)
+{
+       scs_cache = kmem_cache_create("scs_cache", SCS_SIZE, 0, 0, NULL);
+}
+
+int scs_prepare(struct task_struct *tsk, int node)
+{
+       void *s = scs_alloc(node);
+
+       if (!s)
+               return -ENOMEM;
+
+       task_scs(tsk) = task_scs_sp(tsk) = s;
+       return 0;
+}
+
+static void scs_check_usage(struct task_struct *tsk)
+{
+       static unsigned long highest;
+
+       unsigned long *p, prev, curr = highest, used = 0;
+
+       if (!IS_ENABLED(CONFIG_DEBUG_STACK_USAGE))
+               return;
+
+       for (p = task_scs(tsk); p < __scs_magic(tsk); ++p) {
+               if (!READ_ONCE_NOCHECK(*p))
+                       break;
+               used++;
+       }
+
+       while (used > curr) {
+               prev = cmpxchg_relaxed(&highest, curr, used);
+
+               if (prev == curr) {
+                       pr_info("%s (%d): highest shadow stack usage: %lu bytes\n",
+                               tsk->comm, task_pid_nr(tsk), used);
+                       break;
+               }
+
+               curr = prev;
+       }
+}
+
+void scs_release(struct task_struct *tsk)
+{
+       void *s = task_scs(tsk);
+
+       if (!s)
+               return;
+
+       WARN(task_scs_end_corrupted(tsk),
+            "corrupted shadow stack detected when freeing task\n");
+       scs_check_usage(tsk);
+       scs_free(s);
+}
index 284fc1600063bd082c897c125ef4cc20c0612e39..5ca48cc5da760d2a6ad6ef2497cd92be13c4f9f7 100644 (file)
@@ -3235,94 +3235,94 @@ int copy_siginfo_from_user(kernel_siginfo_t *to, const siginfo_t __user *from)
 }
 
 #ifdef CONFIG_COMPAT
-int copy_siginfo_to_user32(struct compat_siginfo __user *to,
-                          const struct kernel_siginfo *from)
-#if defined(CONFIG_X86_X32_ABI) || defined(CONFIG_IA32_EMULATION)
-{
-       return __copy_siginfo_to_user32(to, from, in_x32_syscall());
-}
-int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
-                            const struct kernel_siginfo *from, bool x32_ABI)
-#endif
+/**
+ * copy_siginfo_to_external32 - copy a kernel siginfo into a compat user siginfo
+ * @to: compat siginfo destination
+ * @from: kernel siginfo source
+ *
+ * Note: This function does not work properly for the SIGCHLD on x32, but
+ * fortunately it doesn't have to.  The only valid callers for this function are
+ * copy_siginfo_to_user32, which is overriden for x32 and the coredump code.
+ * The latter does not care because SIGCHLD will never cause a coredump.
+ */
+void copy_siginfo_to_external32(struct compat_siginfo *to,
+               const struct kernel_siginfo *from)
 {
-       struct compat_siginfo new;
-       memset(&new, 0, sizeof(new));
+       memset(to, 0, sizeof(*to));
 
-       new.si_signo = from->si_signo;
-       new.si_errno = from->si_errno;
-       new.si_code  = from->si_code;
+       to->si_signo = from->si_signo;
+       to->si_errno = from->si_errno;
+       to->si_code  = from->si_code;
        switch(siginfo_layout(from->si_signo, from->si_code)) {
        case SIL_KILL:
-               new.si_pid = from->si_pid;
-               new.si_uid = from->si_uid;
+               to->si_pid = from->si_pid;
+               to->si_uid = from->si_uid;
                break;
        case SIL_TIMER:
-               new.si_tid     = from->si_tid;
-               new.si_overrun = from->si_overrun;
-               new.si_int     = from->si_int;
+               to->si_tid     = from->si_tid;
+               to->si_overrun = from->si_overrun;
+               to->si_int     = from->si_int;
                break;
        case SIL_POLL:
-               new.si_band = from->si_band;
-               new.si_fd   = from->si_fd;
+               to->si_band = from->si_band;
+               to->si_fd   = from->si_fd;
                break;
        case SIL_FAULT:
-               new.si_addr = ptr_to_compat(from->si_addr);
+               to->si_addr = ptr_to_compat(from->si_addr);
 #ifdef __ARCH_SI_TRAPNO
-               new.si_trapno = from->si_trapno;
+               to->si_trapno = from->si_trapno;
 #endif
                break;
        case SIL_FAULT_MCEERR:
-               new.si_addr = ptr_to_compat(from->si_addr);
+               to->si_addr = ptr_to_compat(from->si_addr);
 #ifdef __ARCH_SI_TRAPNO
-               new.si_trapno = from->si_trapno;
+               to->si_trapno = from->si_trapno;
 #endif
-               new.si_addr_lsb = from->si_addr_lsb;
+               to->si_addr_lsb = from->si_addr_lsb;
                break;
        case SIL_FAULT_BNDERR:
-               new.si_addr = ptr_to_compat(from->si_addr);
+               to->si_addr = ptr_to_compat(from->si_addr);
 #ifdef __ARCH_SI_TRAPNO
-               new.si_trapno = from->si_trapno;
+               to->si_trapno = from->si_trapno;
 #endif
-               new.si_lower = ptr_to_compat(from->si_lower);
-               new.si_upper = ptr_to_compat(from->si_upper);
+               to->si_lower = ptr_to_compat(from->si_lower);
+               to->si_upper = ptr_to_compat(from->si_upper);
                break;
        case SIL_FAULT_PKUERR:
-               new.si_addr = ptr_to_compat(from->si_addr);
+               to->si_addr = ptr_to_compat(from->si_addr);
 #ifdef __ARCH_SI_TRAPNO
-               new.si_trapno = from->si_trapno;
+               to->si_trapno = from->si_trapno;
 #endif
-               new.si_pkey = from->si_pkey;
+               to->si_pkey = from->si_pkey;
                break;
        case SIL_CHLD:
-               new.si_pid    = from->si_pid;
-               new.si_uid    = from->si_uid;
-               new.si_status = from->si_status;
-#ifdef CONFIG_X86_X32_ABI
-               if (x32_ABI) {
-                       new._sifields._sigchld_x32._utime = from->si_utime;
-                       new._sifields._sigchld_x32._stime = from->si_stime;
-               } else
-#endif
-               {
-                       new.si_utime = from->si_utime;
-                       new.si_stime = from->si_stime;
-               }
+               to->si_pid = from->si_pid;
+               to->si_uid = from->si_uid;
+               to->si_status = from->si_status;
+               to->si_utime = from->si_utime;
+               to->si_stime = from->si_stime;
                break;
        case SIL_RT:
-               new.si_pid = from->si_pid;
-               new.si_uid = from->si_uid;
-               new.si_int = from->si_int;
+               to->si_pid = from->si_pid;
+               to->si_uid = from->si_uid;
+               to->si_int = from->si_int;
                break;
        case SIL_SYS:
-               new.si_call_addr = ptr_to_compat(from->si_call_addr);
-               new.si_syscall   = from->si_syscall;
-               new.si_arch      = from->si_arch;
+               to->si_call_addr = ptr_to_compat(from->si_call_addr);
+               to->si_syscall   = from->si_syscall;
+               to->si_arch      = from->si_arch;
                break;
        }
+}
 
+int __copy_siginfo_to_user32(struct compat_siginfo __user *to,
+                          const struct kernel_siginfo *from)
+{
+       struct compat_siginfo new;
+
+       copy_siginfo_to_external32(&new, from);
        if (copy_to_user(to, &new, sizeof(struct compat_siginfo)))
                return -EFAULT;
-
        return 0;
 }
 
index 786092aabdcd6a997f1bbf428aba111421110bea..84303197caf91270dff29b11dfcc9e0e43ffa742 100644 (file)
@@ -620,7 +620,7 @@ void __init smp_init(void)
  * early_boot_irqs_disabled is set.  Use local_irq_save/restore() instead
  * of local_irq_disable/enable().
  */
-void on_each_cpu(void (*func) (void *info), void *info, int wait)
+void on_each_cpu(smp_call_func_t func, void *info, int wait)
 {
        unsigned long flags;
 
index d325f3ab624a9a0b41ffd83911807d683f0aba22..891667a49bb78496f18f5d9256ef9dfe23604895 100644 (file)
@@ -2262,7 +2262,7 @@ int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which,
        return -EINVAL;
 }
 
-#define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LESS_THROTTLE)
+#define PR_IO_FLUSHER (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE)
 
 SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                unsigned long, arg4, unsigned long, arg5)
@@ -2634,6 +2634,7 @@ struct compat_sysinfo {
 COMPAT_SYSCALL_DEFINE1(sysinfo, struct compat_sysinfo __user *, info)
 {
        struct sysinfo s;
+       struct compat_sysinfo s_32;
 
        do_sysinfo(&s);
 
@@ -2658,23 +2659,23 @@ COMPAT_SYSCALL_DEFINE1(sysinfo, struct compat_sysinfo __user *, info)
                s.freehigh >>= bitcount;
        }
 
-       if (!access_ok(info, sizeof(struct compat_sysinfo)) ||
-           __put_user(s.uptime, &info->uptime) ||
-           __put_user(s.loads[0], &info->loads[0]) ||
-           __put_user(s.loads[1], &info->loads[1]) ||
-           __put_user(s.loads[2], &info->loads[2]) ||
-           __put_user(s.totalram, &info->totalram) ||
-           __put_user(s.freeram, &info->freeram) ||
-           __put_user(s.sharedram, &info->sharedram) ||
-           __put_user(s.bufferram, &info->bufferram) ||
-           __put_user(s.totalswap, &info->totalswap) ||
-           __put_user(s.freeswap, &info->freeswap) ||
-           __put_user(s.procs, &info->procs) ||
-           __put_user(s.totalhigh, &info->totalhigh) ||
-           __put_user(s.freehigh, &info->freehigh) ||
-           __put_user(s.mem_unit, &info->mem_unit))
+       memset(&s_32, 0, sizeof(s_32));
+       s_32.uptime = s.uptime;
+       s_32.loads[0] = s.loads[0];
+       s_32.loads[1] = s.loads[1];
+       s_32.loads[2] = s.loads[2];
+       s_32.totalram = s.totalram;
+       s_32.freeram = s.freeram;
+       s_32.sharedram = s.sharedram;
+       s_32.bufferram = s.bufferram;
+       s_32.totalswap = s.totalswap;
+       s_32.freeswap = s.freeswap;
+       s_32.procs = s.procs;
+       s_32.totalhigh = s.totalhigh;
+       s_32.freehigh = s.freehigh;
+       s_32.mem_unit = s.mem_unit;
+       if (copy_to_user(info, &s_32, sizeof(s_32)))
                return -EFAULT;
-
        return 0;
 }
 #endif /* CONFIG_COMPAT */
index 743647005f64e6aea6946f01e98c25b6851338c7..24876faac753244060b9e3a44a2a97594dde5896 100644 (file)
@@ -10,11 +10,6 @@ config USER_STACKTRACE_SUPPORT
 config NOP_TRACER
        bool
 
-config HAVE_FTRACE_NMI_ENTER
-       bool
-       help
-         See Documentation/trace/ftrace-design.rst
-
 config HAVE_FUNCTION_TRACER
        bool
        help
@@ -72,11 +67,6 @@ config RING_BUFFER
        select TRACE_CLOCK
        select IRQ_WORK
 
-config FTRACE_NMI_ENTER
-       bool
-       depends on HAVE_FTRACE_NMI_ENTER
-       default y
-
 config EVENT_TRACING
        select CONTEXT_SWITCH_TRACER
        select GLOB
@@ -158,6 +148,7 @@ config FUNCTION_TRACER
        select CONTEXT_SWITCH_TRACER
        select GLOB
        select TASKS_RCU if PREEMPTION
+       select TASKS_RUDE_RCU
        help
          Enable the kernel to trace every kernel function. This is done
          by using a compiler feature to insert a small, 5-byte No-Operation
index a010edc37ee02577011f5b385399e9f5376a217f..92ba69b716dc70c087040ec1948e3afaf15c2ea5 100644 (file)
@@ -1500,7 +1500,7 @@ int perf_event_query_prog_array(struct perf_event *event, void __user *info)
        u32 *ids, prog_cnt, ids_len;
        int ret;
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!perfmon_capable())
                return -EPERM;
        if (event->attr.type != PERF_TYPE_TRACEPOINT)
                return -EINVAL;
index bd030b1b9514857cce1e0b38f0f9e2bcce7fa78f..b5765aeea698af5fabf37438e4f9b28703a7a522 100644 (file)
@@ -160,17 +160,6 @@ static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
        op->saved_func(ip, parent_ip, op, regs);
 }
 
-static void ftrace_sync(struct work_struct *work)
-{
-       /*
-        * This function is just a stub to implement a hard force
-        * of synchronize_rcu(). This requires synchronizing
-        * tasks even in userspace and idle.
-        *
-        * Yes, function tracing is rude.
-        */
-}
-
 static void ftrace_sync_ipi(void *data)
 {
        /* Probably not needed, but do it anyway */
@@ -256,7 +245,7 @@ static void update_ftrace_function(void)
         * Make sure all CPUs see this. Yes this is slow, but static
         * tracing is slow and nasty to have enabled.
         */
-       schedule_on_each_cpu(ftrace_sync);
+       synchronize_rcu_tasks_rude();
        /* Now all cpus are using the list ops. */
        function_trace_op = set_function_trace_op;
        /* Make sure the function_trace_op is visible on all CPUs */
@@ -2932,7 +2921,7 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command)
                 * infrastructure to do the synchronization, thus we must do it
                 * ourselves.
                 */
-               schedule_on_each_cpu(ftrace_sync);
+               synchronize_rcu_tasks_rude();
 
                /*
                 * When the kernel is preeptive, tasks can be preempted
@@ -5888,7 +5877,7 @@ ftrace_graph_release(struct inode *inode, struct file *file)
                 * infrastructure to do the synchronization, thus we must do it
                 * ourselves.
                 */
-               schedule_on_each_cpu(ftrace_sync);
+               synchronize_rcu_tasks_rude();
 
                free_ftrace_hash(old_hash);
        }
index 29615f15a820b2f67d66af8084bfcd13936ed9c3..f12e99b387b254e5e154d133fd5b350c47f2fd10 100644 (file)
@@ -8527,18 +8527,6 @@ static int allocate_trace_buffers(struct trace_array *tr, int size)
        allocate_snapshot = false;
 #endif
 
-       /*
-        * Because of some magic with the way alloc_percpu() works on
-        * x86_64, we need to synchronize the pgd of all the tables,
-        * otherwise the trace events that happen in x86_64 page fault
-        * handlers can't cope with accessing the chance that a
-        * alloc_percpu()'d memory might be touched in the page fault trace
-        * event. Oh, and we need to audit all other alloc_percpu() and vmalloc()
-        * calls in tracing, because something might get triggered within a
-        * page fault trace event!
-        */
-       vmalloc_sync_mappings();
-
        return 0;
 }
 
index 5d53f9609c252c1eccaad0e34378794efe0f1998..df3f3da959900616d295d1a18708feca0c2bf817 100644 (file)
@@ -19,6 +19,9 @@ config RAID6_PQ_BENCHMARK
          Benchmark all available RAID6 PQ functions on init and choose the
          fastest one.
 
+config LINEAR_RANGES
+       tristate
+
 config PACKING
        bool "Generic bitfield packing and unpacking"
        default n
@@ -80,6 +83,9 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
        bool
 
+config ARCH_USE_SYM_ANNOTATIONS
+       bool
+
 config INDIRECT_PIO
        bool "Access I/O in non-MMIO mode"
        depends on ARM64
@@ -427,7 +433,7 @@ config INTERVAL_TREE
 
          See:
 
-               Documentation/rbtree.txt
+               Documentation/core-api/rbtree.rst
 
          for more information.
 
index 21d9c5f6e7ec7552be6f46fa352a29394465e3cc..b3b05adab5755e854ec4c4648eb8cc9cdc8a0acf 100644 (file)
@@ -369,6 +369,11 @@ config STACK_VALIDATION
          For more information, see
          tools/objtool/Documentation/stack-validation.txt.
 
+config VMLINUX_VALIDATION
+       bool
+       depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT
+       default y
+
 config DEBUG_FORCE_WEAK_PER_CPU
        bool "Force weak per-cpu definitions"
        depends on DEBUG_KERNEL
@@ -1510,7 +1515,7 @@ config PROVIDE_OHCI1394_DMA_INIT
          This code (~1k) is freed after boot. By then, the firewire stack
          in charge of the OHCI-1394 controllers should be used instead.
 
-         See Documentation/debugging-via-ohci1394.txt for more information.
+         See Documentation/core-api/debugging-via-ohci1394.rst for more information.
 
 source "samples/Kconfig"
 
@@ -2092,6 +2097,18 @@ config LIST_KUNIT_TEST
 
          If unsure, say N.
 
+config LINEAR_RANGES_TEST
+       tristate "KUnit test for linear_ranges"
+       depends on KUNIT
+       select LINEAR_RANGES
+       help
+         This builds the linear_ranges unit test, which runs on boot.
+         Tests the linear_ranges logic correctness.
+         For more information on KUnit and unit tests in general please refer
+         to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+         If unsure, say N.
+
 config TEST_UDELAY
        tristate "udelay test driver"
        help
index 929211039bacd0c0d4ccd2aa7af0a9c69744132f..27bcc2568c95688338c4a47bfcd143115a2321a2 100644 (file)
@@ -63,7 +63,7 @@ config UBSAN_SANITIZE_ALL
 config UBSAN_ALIGNMENT
        bool "Enable checks for pointers alignment"
        default !HAVE_EFFICIENT_UNALIGNED_ACCESS
-       depends on !X86 || !COMPILE_TEST
+       depends on !UBSAN_TRAP
        help
          This option enables the check of unaligned memory accesses.
          Enabling this option on architectures that support unaligned
index 685aee60de1d5ea7d72041e4884e9d5cac1cdaf8..cd548bfa8df9a8de27479c81c80cbeb2e6d071e0 100644 (file)
@@ -125,6 +125,7 @@ obj-$(CONFIG_DEBUG_LIST) += list_debug.o
 obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
 
 obj-$(CONFIG_BITREVERSE) += bitrev.o
+obj-$(CONFIG_LINEAR_RANGES) += linear_ranges.o
 obj-$(CONFIG_PACKING)  += packing.o
 obj-$(CONFIG_CRC_CCITT)        += crc-ccitt.o
 obj-$(CONFIG_CRC16)    += crc16.o
@@ -309,3 +310,4 @@ obj-$(CONFIG_OBJAGG) += objagg.o
 
 # KUnit tests
 obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
+obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
index 89260aa342d685fe0b638a32980521d4226f4429..21a7640c5eedd39c5aa802ff63a8abe40019f619 100644 (file)
@@ -182,21 +182,22 @@ EXPORT_SYMBOL(__bitmap_shift_left);
  *
  * In pictures, example for a big-endian 32-bit architecture:
  *
- * @src:
- * 31                                   63
- * |                                    |
- * 10000000 11000001 11110010 00010101  10000000 11000001 01110010 00010101
- *                 |  |              |                                    |
- *                16  14             0                                   32
- *
- * if @cut is 3, and @first is 14, bits 14-16 in @src are cut and @dst is:
- *
- * 31                                   63
- * |                                    |
- * 10110000 00011000 00110010 00010101  00010000 00011000 00101110 01000010
- *                    |              |                                    |
- *                    14 (bit 17     0                                   32
- *                        from @src)
+ * The @src bitmap is::
+ *
+ *   31                                   63
+ *   |                                    |
+ *   10000000 11000001 11110010 00010101  10000000 11000001 01110010 00010101
+ *                   |  |              |                                    |
+ *                  16  14             0                                   32
+ *
+ * if @cut is 3, and @first is 14, bits 14-16 in @src are cut and @dst is::
+ *
+ *   31                                   63
+ *   |                                    |
+ *   10110000 00011000 00110010 00010101  00010000 00011000 00101110 01000010
+ *                      |              |                                    |
+ *                      14 (bit 17     0                                   32
+ *                          from @src)
  *
  * Note that @dst and @src might overlap partially or entirely.
  *
index de032ad96f4ac0e82b3028fe41e523a7636d2170..7ac65a0000ff096d56b9063adaa7f551f596686a 100644 (file)
@@ -145,26 +145,6 @@ __sum16 ip_compute_csum(const void *buff, int len)
 }
 EXPORT_SYMBOL(ip_compute_csum);
 
-/*
- * copy from fs while checksumming, otherwise like csum_partial
- */
-__wsum
-csum_partial_copy_from_user(const void __user *src, void *dst, int len,
-                                               __wsum sum, int *csum_err)
-{
-       int missing;
-
-       missing = __copy_from_user(dst, src, len);
-       if (missing) {
-               memset(dst + len - missing, 0, missing);
-               *csum_err = -EFAULT;
-       } else
-               *csum_err = 0;
-
-       return csum_partial(dst, len, sum);
-}
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-
 /*
  * copy from ds while checksumming, otherwise like csum_partial
  */
index 65ead6b0c7e000293a34320dbbc640d2b71d0a9c..4ccbec442469c30a4fed329852fb421e7dd57c20 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/export.h>
 #include <linux/bitops.h>
 #include <linux/string.h>
-#include <linux/cryptohash.h>
 #include <asm/unaligned.h>
 #include <crypto/chacha.h>
 
index 66cb04b0cf4e7e57ad1b241c134c02ea99f454ca..2e621697c5c35c2769ce179e8d08da7aa987c1c5 100644 (file)
@@ -206,7 +206,7 @@ static void sha256_transform(u32 *state, const u8 *input)
        memzero_explicit(W, 64 * sizeof(u32));
 }
 
-int sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len)
+void sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len)
 {
        unsigned int partial, done;
        const u8 *src;
@@ -232,18 +232,16 @@ int sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len)
                partial = 0;
        }
        memcpy(sctx->buf + partial, src, len - done);
-
-       return 0;
 }
 EXPORT_SYMBOL(sha256_update);
 
-int sha224_update(struct sha256_state *sctx, const u8 *data, unsigned int len)
+void sha224_update(struct sha256_state *sctx, const u8 *data, unsigned int len)
 {
-       return sha256_update(sctx, data, len);
+       sha256_update(sctx, data, len);
 }
 EXPORT_SYMBOL(sha224_update);
 
-static int __sha256_final(struct sha256_state *sctx, u8 *out, int digest_words)
+static void __sha256_final(struct sha256_state *sctx, u8 *out, int digest_words)
 {
        __be32 *dst = (__be32 *)out;
        __be64 bits;
@@ -268,19 +266,17 @@ static int __sha256_final(struct sha256_state *sctx, u8 *out, int digest_words)
 
        /* Zeroize sensitive information. */
        memset(sctx, 0, sizeof(*sctx));
-
-       return 0;
 }
 
-int sha256_final(struct sha256_state *sctx, u8 *out)
+void sha256_final(struct sha256_state *sctx, u8 *out)
 {
-       return __sha256_final(sctx, out, 8);
+       __sha256_final(sctx, out, 8);
 }
 EXPORT_SYMBOL(sha256_final);
 
-int sha224_final(struct sha256_state *sctx, u8 *out)
+void sha224_final(struct sha256_state *sctx, u8 *out)
 {
-       return __sha256_final(sctx, out, 7);
+       __sha256_final(sctx, out, 7);
 }
 EXPORT_SYMBOL(sha224_final);
 
index 3f0e18543de84f9b94d64a8a6d79ef5e35ea8d7a..ad485f08173bdc260130b777df1d6fb66409e4a7 100644 (file)
@@ -61,13 +61,14 @@ static inline int ioremap_pmd_enabled(void) { return 0; }
 #endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
 
 static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
-               unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
+               unsigned long end, phys_addr_t phys_addr, pgprot_t prot,
+               pgtbl_mod_mask *mask)
 {
        pte_t *pte;
        u64 pfn;
 
        pfn = phys_addr >> PAGE_SHIFT;
-       pte = pte_alloc_kernel(pmd, addr);
+       pte = pte_alloc_kernel_track(pmd, addr, mask);
        if (!pte)
                return -ENOMEM;
        do {
@@ -75,6 +76,7 @@ static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
                set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
                pfn++;
        } while (pte++, addr += PAGE_SIZE, addr != end);
+       *mask |= PGTBL_PTE_MODIFIED;
        return 0;
 }
 
@@ -101,21 +103,24 @@ static int ioremap_try_huge_pmd(pmd_t *pmd, unsigned long addr,
 }
 
 static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
-               unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
+               unsigned long end, phys_addr_t phys_addr, pgprot_t prot,
+               pgtbl_mod_mask *mask)
 {
        pmd_t *pmd;
        unsigned long next;
 
-       pmd = pmd_alloc(&init_mm, pud, addr);
+       pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
        if (!pmd)
                return -ENOMEM;
        do {
                next = pmd_addr_end(addr, end);
 
-               if (ioremap_try_huge_pmd(pmd, addr, next, phys_addr, prot))
+               if (ioremap_try_huge_pmd(pmd, addr, next, phys_addr, prot)) {
+                       *mask |= PGTBL_PMD_MODIFIED;
                        continue;
+               }
 
-               if (ioremap_pte_range(pmd, addr, next, phys_addr, prot))
+               if (ioremap_pte_range(pmd, addr, next, phys_addr, prot, mask))
                        return -ENOMEM;
        } while (pmd++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
@@ -144,21 +149,24 @@ static int ioremap_try_huge_pud(pud_t *pud, unsigned long addr,
 }
 
 static inline int ioremap_pud_range(p4d_t *p4d, unsigned long addr,
-               unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
+               unsigned long end, phys_addr_t phys_addr, pgprot_t prot,
+               pgtbl_mod_mask *mask)
 {
        pud_t *pud;
        unsigned long next;
 
-       pud = pud_alloc(&init_mm, p4d, addr);
+       pud = pud_alloc_track(&init_mm, p4d, addr, mask);
        if (!pud)
                return -ENOMEM;
        do {
                next = pud_addr_end(addr, end);
 
-               if (ioremap_try_huge_pud(pud, addr, next, phys_addr, prot))
+               if (ioremap_try_huge_pud(pud, addr, next, phys_addr, prot)) {
+                       *mask |= PGTBL_PUD_MODIFIED;
                        continue;
+               }
 
-               if (ioremap_pmd_range(pud, addr, next, phys_addr, prot))
+               if (ioremap_pmd_range(pud, addr, next, phys_addr, prot, mask))
                        return -ENOMEM;
        } while (pud++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
@@ -187,21 +195,24 @@ static int ioremap_try_huge_p4d(p4d_t *p4d, unsigned long addr,
 }
 
 static inline int ioremap_p4d_range(pgd_t *pgd, unsigned long addr,
-               unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
+               unsigned long end, phys_addr_t phys_addr, pgprot_t prot,
+               pgtbl_mod_mask *mask)
 {
        p4d_t *p4d;
        unsigned long next;
 
-       p4d = p4d_alloc(&init_mm, pgd, addr);
+       p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
        if (!p4d)
                return -ENOMEM;
        do {
                next = p4d_addr_end(addr, end);
 
-               if (ioremap_try_huge_p4d(p4d, addr, next, phys_addr, prot))
+               if (ioremap_try_huge_p4d(p4d, addr, next, phys_addr, prot)) {
+                       *mask |= PGTBL_P4D_MODIFIED;
                        continue;
+               }
 
-               if (ioremap_pud_range(p4d, addr, next, phys_addr, prot))
+               if (ioremap_pud_range(p4d, addr, next, phys_addr, prot, mask))
                        return -ENOMEM;
        } while (p4d++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
@@ -214,6 +225,7 @@ int ioremap_page_range(unsigned long addr,
        unsigned long start;
        unsigned long next;
        int err;
+       pgtbl_mod_mask mask = 0;
 
        might_sleep();
        BUG_ON(addr >= end);
@@ -222,13 +234,17 @@ int ioremap_page_range(unsigned long addr,
        pgd = pgd_offset_k(addr);
        do {
                next = pgd_addr_end(addr, end);
-               err = ioremap_p4d_range(pgd, addr, next, phys_addr, prot);
+               err = ioremap_p4d_range(pgd, addr, next, phys_addr, prot,
+                                       &mask);
                if (err)
                        break;
        } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
 
        flush_cache_vmap(start, end);
 
+       if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
+               arch_sync_kernel_mappings(start, end);
+
        return err;
 }
 
index 83198cb37d8d93b6ac5263b16b4a9c72e5a958ef..65fa7bf70c57acc873f42ed60e740b91196c61af 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2006-2007 Greg Kroah-Hartman <greg@kroah.com>
  * Copyright (c) 2006-2007 Novell Inc.
  *
- * Please see the file Documentation/kobject.txt for critical information
+ * Please see the file Documentation/core-api/kobject.rst for critical information
  * about using the kobject interface.
  */
 
@@ -670,7 +670,7 @@ static void kobject_cleanup(struct kobject *kobj)
                 kobject_name(kobj), kobj, __func__, kobj->parent);
 
        if (t && !t->release)
-               pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/kobject.txt.\n",
+               pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n",
                         kobject_name(kobj), kobj);
 
        /* send "remove" if the caller did not do it but sent "add" */
diff --git a/lib/linear_ranges.c b/lib/linear_ranges.c
new file mode 100644 (file)
index 0000000..9495ef3
--- /dev/null
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * helpers to map values in a linear range to range index
+ *
+ * Original idea borrowed from regulator framework
+ *
+ * It might be useful if we could support also inversely proportional ranges?
+ * Copyright 2020 ROHM Semiconductors
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/linear_range.h>
+#include <linux/module.h>
+
+/**
+ * linear_range_values_in_range - return the amount of values in a range
+ * @r:         pointer to linear range where values are counted
+ *
+ * Compute the amount of values in range pointed by @r. Note, values can
+ * be all equal - range with selectors 0,...,2 with step 0 still contains
+ * 3 values even though they are all equal.
+ *
+ * Return: the amount of values in range pointed by @r
+ */
+unsigned int linear_range_values_in_range(const struct linear_range *r)
+{
+       if (!r)
+               return 0;
+       return r->max_sel - r->min_sel + 1;
+}
+EXPORT_SYMBOL_GPL(linear_range_values_in_range);
+
+/**
+ * linear_range_values_in_range_array - return the amount of values in ranges
+ * @r:         pointer to array of linear ranges where values are counted
+ * @ranges:    amount of ranges we include in computation.
+ *
+ * Compute the amount of values in ranges pointed by @r. Note, values can
+ * be all equal - range with selectors 0,...,2 with step 0 still contains
+ * 3 values even though they are all equal.
+ *
+ * Return: the amount of values in first @ranges ranges pointed by @r
+ */
+unsigned int linear_range_values_in_range_array(const struct linear_range *r,
+                                               int ranges)
+{
+       int i, values_in_range = 0;
+
+       for (i = 0; i < ranges; i++) {
+               int values;
+
+               values = linear_range_values_in_range(&r[i]);
+               if (!values)
+                       return values;
+
+               values_in_range += values;
+       }
+       return values_in_range;
+}
+EXPORT_SYMBOL_GPL(linear_range_values_in_range_array);
+
+/**
+ * linear_range_get_max_value - return the largest value in a range
+ * @r:         pointer to linear range where value is looked from
+ *
+ * Return: the largest value in the given range
+ */
+unsigned int linear_range_get_max_value(const struct linear_range *r)
+{
+       return r->min + (r->max_sel - r->min_sel) * r->step;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_max_value);
+
+/**
+ * linear_range_get_value - fetch a value from given range
+ * @r:         pointer to linear range where value is looked from
+ * @selector:  selector for which the value is searched
+ * @val:       address where found value is updated
+ *
+ * Search given ranges for value which matches given selector.
+ *
+ * Return: 0 on success, -EINVAL given selector is not found from any of the
+ * ranges.
+ */
+int linear_range_get_value(const struct linear_range *r, unsigned int selector,
+                          unsigned int *val)
+{
+       if (r->min_sel > selector || r->max_sel < selector)
+               return -EINVAL;
+
+       *val = r->min + (selector - r->min_sel) * r->step;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_value);
+
+/**
+ * linear_range_get_value_array - fetch a value from array of ranges
+ * @r:         pointer to array of linear ranges where value is looked from
+ * @ranges:    amount of ranges in an array
+ * @selector:  selector for which the value is searched
+ * @val:       address where found value is updated
+ *
+ * Search through an array of ranges for value which matches given selector.
+ *
+ * Return: 0 on success, -EINVAL given selector is not found from any of the
+ * ranges.
+ */
+int linear_range_get_value_array(const struct linear_range *r, int ranges,
+                                unsigned int selector, unsigned int *val)
+{
+       int i;
+
+       for (i = 0; i < ranges; i++)
+               if (r[i].min_sel <= selector && r[i].max_sel >= selector)
+                       return linear_range_get_value(&r[i], selector, val);
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_value_array);
+
+/**
+ * linear_range_get_selector_low - return linear range selector for value
+ * @r:         pointer to linear range where selector is looked from
+ * @val:       value for which the selector is searched
+ * @selector:  address where found selector value is updated
+ * @found:     flag to indicate that given value was in the range
+ *
+ * Return selector which which range value is closest match for given
+ * input value. Value is matching if it is equal or smaller than given
+ * value. If given value is in the range, then @found is set true.
+ *
+ * Return: 0 on success, -EINVAL if range is invalid or does not contain
+ * value smaller or equal to given value
+ */
+int linear_range_get_selector_low(const struct linear_range *r,
+                                 unsigned int val, unsigned int *selector,
+                                 bool *found)
+{
+       *found = false;
+
+       if (r->min > val)
+               return -EINVAL;
+
+       if (linear_range_get_max_value(r) < val) {
+               *selector = r->max_sel;
+               return 0;
+       }
+
+       *found = true;
+
+       if (r->step == 0)
+               *selector = r->min_sel;
+       else
+               *selector = (val - r->min) / r->step + r->min_sel;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
+
+/**
+ * linear_range_get_selector_low_array - return linear range selector for value
+ * @r:         pointer to array of linear ranges where selector is looked from
+ * @ranges:    amount of ranges to scan from array
+ * @val:       value for which the selector is searched
+ * @selector:  address where found selector value is updated
+ * @found:     flag to indicate that given value was in the range
+ *
+ * Scan array of ranges for selector which which range value matches given
+ * input value. Value is matching if it is equal or smaller than given
+ * value. If given value is found to be in a range scanning is stopped and
+ * @found is set true. If a range with values smaller than given value is found
+ * but the range max is being smaller than given value, then the ranges
+ * biggest selector is updated to @selector but scanning ranges is continued
+ * and @found is set to false.
+ *
+ * Return: 0 on success, -EINVAL if range array is invalid or does not contain
+ * range with a value smaller or equal to given value
+ */
+int linear_range_get_selector_low_array(const struct linear_range *r,
+                                       int ranges, unsigned int val,
+                                       unsigned int *selector, bool *found)
+{
+       int i;
+       int ret = -EINVAL;
+
+       for (i = 0; i < ranges; i++) {
+               int tmpret;
+
+               tmpret = linear_range_get_selector_low(&r[i], val, selector,
+                                                      found);
+               if (!tmpret)
+                       ret = 0;
+
+               if (*found)
+                       break;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
+
+/**
+ * linear_range_get_selector_high - return linear range selector for value
+ * @r:         pointer to linear range where selector is looked from
+ * @val:       value for which the selector is searched
+ * @selector:  address where found selector value is updated
+ * @found:     flag to indicate that given value was in the range
+ *
+ * Return selector which which range value is closest match for given
+ * input value. Value is matching if it is equal or higher than given
+ * value. If given value is in the range, then @found is set true.
+ *
+ * Return: 0 on success, -EINVAL if range is invalid or does not contain
+ * value greater or equal to given value
+ */
+int linear_range_get_selector_high(const struct linear_range *r,
+                                  unsigned int val, unsigned int *selector,
+                                  bool *found)
+{
+       *found = false;
+
+       if (linear_range_get_max_value(r) < val)
+               return -EINVAL;
+
+       if (r->min > val) {
+               *selector = r->min_sel;
+               return 0;
+       }
+
+       *found = true;
+
+       if (r->step == 0)
+               *selector = r->max_sel;
+       else
+               *selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
+
+MODULE_DESCRIPTION("linear-ranges helper");
+MODULE_LICENSE("GPL");
index 891e1c3549c465a6d18e86e79437b5bdb3aca824..afbd99987cf89d763cc04c6eac0fe1b9266db7f4 100644 (file)
@@ -653,7 +653,7 @@ do {                                                \
        **************  MIPS/64  **************
        ***************************************/
 #if (defined(__mips) && __mips >= 3) && W_TYPE_SIZE == 64
-#if defined(__mips_isa_rev) && __mips_isa_rev >= 6
+#if defined(__mips_isa_rev) && __mips_isa_rev >= 6 && defined(CONFIG_CC_IS_GCC)
 /*
  * GCC ends up emitting a __multi3 intrinsic call for MIPS64r6 with the plain C
  * code below, so we special case MIPS64r6 until the compiler can do better.
index 2ee6ae3b0ade09d197d3cb8859db3b7c570f51e5..34e406fe561fe277f04cc2354ba59ab0c7fb4af5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kernel.h>
 #include <linux/kmemleak.h>
 #include <linux/percpu.h>
+#include <linux/local_lock.h>
 #include <linux/preempt.h>             /* in_interrupt() */
 #include <linux/radix-tree.h>
 #include <linux/rcupdate.h>
@@ -27,7 +28,6 @@
 #include <linux/string.h>
 #include <linux/xarray.h>
 
-
 /*
  * Radix tree node cache.
  */
@@ -58,12 +58,10 @@ struct kmem_cache *radix_tree_node_cachep;
 /*
  * Per-cpu pool of preloaded nodes
  */
-struct radix_tree_preload {
-       unsigned nr;
-       /* nodes->parent points to next preallocated node */
-       struct radix_tree_node *nodes;
+DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = {
+       .lock = INIT_LOCAL_LOCK(lock),
 };
-static DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, };
+EXPORT_PER_CPU_SYMBOL_GPL(radix_tree_preloads);
 
 static inline struct radix_tree_node *entry_to_node(void *ptr)
 {
@@ -332,14 +330,14 @@ static __must_check int __radix_tree_preload(gfp_t gfp_mask, unsigned nr)
         */
        gfp_mask &= ~__GFP_ACCOUNT;
 
-       preempt_disable();
+       local_lock(&radix_tree_preloads.lock);
        rtp = this_cpu_ptr(&radix_tree_preloads);
        while (rtp->nr < nr) {
-               preempt_enable();
+               local_unlock(&radix_tree_preloads.lock);
                node = kmem_cache_alloc(radix_tree_node_cachep, gfp_mask);
                if (node == NULL)
                        goto out;
-               preempt_disable();
+               local_lock(&radix_tree_preloads.lock);
                rtp = this_cpu_ptr(&radix_tree_preloads);
                if (rtp->nr < nr) {
                        node->parent = rtp->nodes;
@@ -381,7 +379,7 @@ int radix_tree_maybe_preload(gfp_t gfp_mask)
        if (gfpflags_allow_blocking(gfp_mask))
                return __radix_tree_preload(gfp_mask, RADIX_TREE_PRELOAD_SIZE);
        /* Preloading doesn't help anything with this gfp mask, skip it */
-       preempt_disable();
+       local_lock(&radix_tree_preloads.lock);
        return 0;
 }
 EXPORT_SYMBOL(radix_tree_maybe_preload);
@@ -1470,7 +1468,7 @@ EXPORT_SYMBOL(radix_tree_tagged);
 void idr_preload(gfp_t gfp_mask)
 {
        if (__radix_tree_preload(gfp_mask, IDR_PRELOAD_SIZE))
-               preempt_disable();
+               local_lock(&radix_tree_preloads.lock);
 }
 EXPORT_SYMBOL(idr_preload);
 
index 1d96d2c02b826972f157d4e436dabf521e44e4af..49257a915bb604e3bb17e53b0ef86e3a855ad9ca 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/bitops.h>
-#include <linux/cryptohash.h>
+#include <crypto/sha.h>
 #include <asm/unaligned.h>
 
 /*
 #define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) ,  0xca62c1d6, A, B, C, D, E )
 
 /**
- * sha_transform - single block SHA1 transform
+ * sha1_transform - single block SHA1 transform (deprecated)
  *
  * @digest: 160 bit digest to update
  * @data:   512 bits of data to hash
  * @array:  16 words of workspace (see note)
  *
- * This function generates a SHA1 digest for a single 512-bit block.
- * Be warned, it does not handle padding and message digest, do not
- * confuse it with the full FIPS 180-1 digest algorithm for variable
- * length messages.
+ * This function executes SHA-1's internal compression function.  It updates the
+ * 160-bit internal state (@digest) with a single 512-bit data block (@data).
+ *
+ * Don't use this function.  SHA-1 is no longer considered secure.  And even if
+ * you do have to use SHA-1, this isn't the correct way to hash something with
+ * SHA-1 as this doesn't handle padding and finalization.
  *
  * Note: If the hash is security sensitive, the caller should be sure
  * to clear the workspace. This is left to the caller to avoid
  * unnecessary clears between chained hashing operations.
  */
-void sha_transform(__u32 *digest, const char *data, __u32 *array)
+void sha1_transform(__u32 *digest, const char *data, __u32 *array)
 {
        __u32 A, B, C, D, E;
 
@@ -185,13 +187,13 @@ void sha_transform(__u32 *digest, const char *data, __u32 *array)
        digest[3] += D;
        digest[4] += E;
 }
-EXPORT_SYMBOL(sha_transform);
+EXPORT_SYMBOL(sha1_transform);
 
 /**
- * sha_init - initialize the vectors for a SHA1 digest
+ * sha1_init - initialize the vectors for a SHA1 digest
  * @buf: vector to initialize
  */
-void sha_init(__u32 *buf)
+void sha1_init(__u32 *buf)
 {
        buf[0] = 0x67452301;
        buf[1] = 0xefcdab89;
@@ -199,4 +201,4 @@ void sha_init(__u32 *buf)
        buf[3] = 0x10325476;
        buf[4] = 0xc3d2e1f0;
 }
-EXPORT_SYMBOL(sha_init);
+EXPORT_SYMBOL(sha1_init);
index 706020b06617ce6fae8e0e9517259a6a0fcfcb78..b90ec550183ae82196467bf161ec7de1f4eee877 100644 (file)
@@ -116,9 +116,9 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
 
                kasan_check_write(dst, count);
                check_object_size(dst, count, false);
-               if (user_access_begin(src, max)) {
+               if (user_read_access_begin(src, max)) {
                        retval = do_strncpy_from_user(dst, src, count, max);
-                       user_access_end();
+                       user_read_access_end();
                        return retval;
                }
        }
index 41670d4a5816530d7b948fe29f0fad20a44ec2ed..1616710b8a82cf93f3ad4f1fa41f6ea35167dae1 100644 (file)
@@ -109,9 +109,9 @@ long strnlen_user(const char __user *str, long count)
                if (max > count)
                        max = count;
 
-               if (user_access_begin(str, max)) {
+               if (user_read_access_begin(str, max)) {
                        retval = do_strnlen_user(str, count, max);
-                       user_access_end();
+                       user_read_access_end();
                        return retval;
                }
        }
diff --git a/lib/test_linear_ranges.c b/lib/test_linear_ranges.c
new file mode 100644 (file)
index 0000000..676e0b8
--- /dev/null
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for the linear_ranges helper.
+ *
+ * Copyright (C) 2020, ROHM Semiconductors.
+ * Author: Matti Vaittinen <matti.vaittien@fi.rohmeurope.com>
+ */
+#include <kunit/test.h>
+
+#include <linux/linear_range.h>
+
+/* First things first. I deeply dislike unit-tests. I have seen all the hell
+ * breaking loose when people who think the unit tests are "the silver bullet"
+ * to kill bugs get to decide how a company should implement testing strategy...
+ *
+ * Believe me, it may get _really_ ridiculous. It is tempting to think that
+ * walking through all the possible execution branches will nail down 100% of
+ * bugs. This may lead to ideas about demands to get certain % of "test
+ * coverage" - measured as line coverage. And that is one of the worst things
+ * you can do.
+ *
+ * Ask people to provide line coverage and they do. I've seen clever tools
+ * which generate test cases to test the existing functions - and by default
+ * these tools expect code to be correct and just generate checks which are
+ * passing when ran against current code-base. Run this generator and you'll get
+ * tests that do not test code is correct but just verify nothing changes.
+ * Problem is that testing working code is pointless. And if it is not
+ * working, your test must not assume it is working. You won't catch any bugs
+ * by such tests. What you can do is to generate a huge amount of tests.
+ * Especially if you were are asked to proivde 100% line-coverage x_x. So what
+ * does these tests - which are not finding any bugs now - do?
+ *
+ * They add inertia to every future development. I think it was Terry Pratchet
+ * who wrote someone having same impact as thick syrup has to chronometre.
+ * Excessive amount of unit-tests have this effect to development. If you do
+ * actually find _any_ bug from code in such environment and try fixing it...
+ * ...chances are you also need to fix the test cases. In sunny day you fix one
+ * test. But I've done refactoring which resulted 500+ broken tests (which had
+ * really zero value other than proving to managers that we do do "quality")...
+ *
+ * After this being said - there are situations where UTs can be handy. If you
+ * have algorithms which take some input and should produce output - then you
+ * can implement few, carefully selected simple UT-cases which test this. I've
+ * previously used this for example for netlink and device-tree data parsing
+ * functions. Feed some data examples to functions and verify the output is as
+ * expected. I am not covering all the cases but I will see the logic should be
+ * working.
+ *
+ * Here we also do some minor testing. I don't want to go through all branches
+ * or test more or less obvious things - but I want to see the main logic is
+ * working. And I definitely don't want to add 500+ test cases that break when
+ * some simple fix is done x_x. So - let's only add few, well selected tests
+ * which ensure as much logic is good as possible.
+ */
+
+/*
+ * Test Range 1:
+ * selectors:  2       3       4       5       6
+ * values (5): 10      20      30      40      50
+ *
+ * Test Range 2:
+ * selectors:  7       8       9       10
+ * values (4): 100     150     200     250
+ */
+
+#define RANGE1_MIN 10
+#define RANGE1_MIN_SEL 2
+#define RANGE1_STEP 10
+
+/* 2, 3, 4, 5, 6 */
+static const unsigned int range1_sels[] = { RANGE1_MIN_SEL, RANGE1_MIN_SEL + 1,
+                                           RANGE1_MIN_SEL + 2,
+                                           RANGE1_MIN_SEL + 3,
+                                           RANGE1_MIN_SEL + 4 };
+/* 10, 20, 30, 40, 50 */
+static const unsigned int range1_vals[] = { RANGE1_MIN, RANGE1_MIN +
+                                           RANGE1_STEP,
+                                           RANGE1_MIN + RANGE1_STEP * 2,
+                                           RANGE1_MIN + RANGE1_STEP * 3,
+                                           RANGE1_MIN + RANGE1_STEP * 4 };
+
+#define RANGE2_MIN 100
+#define RANGE2_MIN_SEL 7
+#define RANGE2_STEP 50
+
+/*  7, 8, 9, 10 */
+static const unsigned int range2_sels[] = { RANGE2_MIN_SEL, RANGE2_MIN_SEL + 1,
+                                           RANGE2_MIN_SEL + 2,
+                                           RANGE2_MIN_SEL + 3 };
+/* 100, 150, 200, 250 */
+static const unsigned int range2_vals[] = { RANGE2_MIN, RANGE2_MIN +
+                                           RANGE2_STEP,
+                                           RANGE2_MIN + RANGE2_STEP * 2,
+                                           RANGE2_MIN + RANGE2_STEP * 3 };
+
+#define RANGE1_NUM_VALS (ARRAY_SIZE(range1_vals))
+#define RANGE2_NUM_VALS (ARRAY_SIZE(range2_vals))
+#define RANGE_NUM_VALS (RANGE1_NUM_VALS + RANGE2_NUM_VALS)
+
+#define RANGE1_MAX_SEL (RANGE1_MIN_SEL + RANGE1_NUM_VALS - 1)
+#define RANGE1_MAX_VAL (range1_vals[RANGE1_NUM_VALS - 1])
+
+#define RANGE2_MAX_SEL (RANGE2_MIN_SEL + RANGE2_NUM_VALS - 1)
+#define RANGE2_MAX_VAL (range2_vals[RANGE2_NUM_VALS - 1])
+
+#define SMALLEST_SEL RANGE1_MIN_SEL
+#define SMALLEST_VAL RANGE1_MIN
+
+static struct linear_range testr[] = {
+       {
+               .min = RANGE1_MIN,
+               .min_sel = RANGE1_MIN_SEL,
+               .max_sel = RANGE1_MAX_SEL,
+               .step = RANGE1_STEP,
+       }, {
+               .min = RANGE2_MIN,
+               .min_sel = RANGE2_MIN_SEL,
+               .max_sel = RANGE2_MAX_SEL,
+               .step = RANGE2_STEP
+       },
+};
+
+static void range_test_get_value(struct kunit *test)
+{
+       int ret, i;
+       unsigned int sel, val;
+
+       for (i = 0; i < RANGE1_NUM_VALS; i++) {
+               sel = range1_sels[i];
+               ret = linear_range_get_value_array(&testr[0], 2, sel, &val);
+               KUNIT_EXPECT_EQ(test, 0, ret);
+               KUNIT_EXPECT_EQ(test, val, range1_vals[i]);
+       }
+       for (i = 0; i < RANGE2_NUM_VALS; i++) {
+               sel = range2_sels[i];
+               ret = linear_range_get_value_array(&testr[0], 2, sel, &val);
+               KUNIT_EXPECT_EQ(test, 0, ret);
+               KUNIT_EXPECT_EQ(test, val, range2_vals[i]);
+       }
+       ret = linear_range_get_value_array(&testr[0], 2, sel + 1, &val);
+       KUNIT_EXPECT_NE(test, 0, ret);
+}
+
+static void range_test_get_selector_high(struct kunit *test)
+{
+       int ret, i;
+       unsigned int sel;
+       bool found;
+
+       for (i = 0; i < RANGE1_NUM_VALS; i++) {
+               ret = linear_range_get_selector_high(&testr[0], range1_vals[i],
+                                                    &sel, &found);
+               KUNIT_EXPECT_EQ(test, 0, ret);
+               KUNIT_EXPECT_EQ(test, sel, range1_sels[i]);
+               KUNIT_EXPECT_TRUE(test, found);
+       }
+
+       ret = linear_range_get_selector_high(&testr[0], RANGE1_MAX_VAL + 1,
+                                            &sel, &found);
+       KUNIT_EXPECT_LE(test, ret, 0);
+
+       ret = linear_range_get_selector_high(&testr[0], RANGE1_MIN - 1,
+                                            &sel, &found);
+       KUNIT_EXPECT_EQ(test, 0, ret);
+       KUNIT_EXPECT_FALSE(test, found);
+       KUNIT_EXPECT_EQ(test, sel, range1_sels[0]);
+}
+
+static void range_test_get_value_amount(struct kunit *test)
+{
+       int ret;
+
+       ret = linear_range_values_in_range_array(&testr[0], 2);
+       KUNIT_EXPECT_EQ(test, (int)RANGE_NUM_VALS, ret);
+}
+
+static void range_test_get_selector_low(struct kunit *test)
+{
+       int i, ret;
+       unsigned int sel;
+       bool found;
+
+       for (i = 0; i < RANGE1_NUM_VALS; i++) {
+               ret = linear_range_get_selector_low_array(&testr[0], 2,
+                                                         range1_vals[i], &sel,
+                                                         &found);
+               KUNIT_EXPECT_EQ(test, 0, ret);
+               KUNIT_EXPECT_EQ(test, sel, range1_sels[i]);
+               KUNIT_EXPECT_TRUE(test, found);
+       }
+       for (i = 0; i < RANGE2_NUM_VALS; i++) {
+               ret = linear_range_get_selector_low_array(&testr[0], 2,
+                                                         range2_vals[i], &sel,
+                                                         &found);
+               KUNIT_EXPECT_EQ(test, 0, ret);
+               KUNIT_EXPECT_EQ(test, sel, range2_sels[i]);
+               KUNIT_EXPECT_TRUE(test, found);
+       }
+
+       /*
+        * Seek value greater than range max => get_selector_*_low should
+        * return Ok - but set found to false as value is not in range
+        */
+       ret = linear_range_get_selector_low_array(&testr[0], 2,
+                                       range2_vals[RANGE2_NUM_VALS - 1] + 1,
+                                       &sel, &found);
+
+       KUNIT_EXPECT_EQ(test, 0, ret);
+       KUNIT_EXPECT_EQ(test, sel, range2_sels[RANGE2_NUM_VALS - 1]);
+       KUNIT_EXPECT_FALSE(test, found);
+}
+
+static struct kunit_case range_test_cases[] = {
+       KUNIT_CASE(range_test_get_value_amount),
+       KUNIT_CASE(range_test_get_selector_high),
+       KUNIT_CASE(range_test_get_selector_low),
+       KUNIT_CASE(range_test_get_value),
+       {},
+};
+
+static struct kunit_suite range_test_module = {
+       .name = "linear-ranges-test",
+       .test_cases = range_test_cases,
+};
+
+kunit_test_suites(&range_test_module);
+
+MODULE_LICENSE("GPL");
index 6b1622f4d7c2536efc743e9ee9e00f710a3d63d3..7d60f24240a43f66bd0f66d66d6094d46e5bdda0 100644 (file)
@@ -494,7 +494,7 @@ struct_va_format(void)
 }
 
 static void __init
-struct_rtc_time(void)
+time_and_date(void)
 {
        /* 1543210543 */
        const struct rtc_time tm = {
@@ -505,14 +505,21 @@ struct_rtc_time(void)
                .tm_mon = 10,
                .tm_year = 118,
        };
+       /* 2019-01-04T15:32:23 */
+       time64_t t = 1546615943;
 
-       test("(%ptR?)", "%pt", &tm);
+       test("(%pt?)", "%pt", &tm);
        test("2018-11-26T05:35:43", "%ptR", &tm);
        test("0118-10-26T05:35:43", "%ptRr", &tm);
        test("05:35:43|2018-11-26", "%ptRt|%ptRd", &tm, &tm);
        test("05:35:43|0118-10-26", "%ptRtr|%ptRdr", &tm, &tm);
        test("05:35:43|2018-11-26", "%ptRttr|%ptRdtr", &tm, &tm);
        test("05:35:43 tr|2018-11-26 tr", "%ptRt tr|%ptRd tr", &tm, &tm);
+
+       test("2019-01-04T15:32:23", "%ptT", &t);
+       test("0119-00-04T15:32:23", "%ptTr", &t);
+       test("15:32:23|2019-01-04", "%ptTt|%ptTd", &t, &t);
+       test("15:32:23|0119-00-04", "%ptTtr|%ptTdr", &t, &t);
 }
 
 static void __init
@@ -678,7 +685,7 @@ test_pointer(void)
        uuid();
        dentry();
        struct_va_format();
-       struct_rtc_time();
+       time_and_date();
        struct_clk();
        bitmap();
        netdev_features();
index 8bbefcaddfe843f65dae4fcd7ac77867fc53eb18..ddc9685702b15a203cb39029ad8889a1cc716291 100644 (file)
@@ -91,12 +91,8 @@ static int random_size_align_alloc_test(void)
                 */
                size = ((rnd % 10) + 1) * PAGE_SIZE;
 
-               ptr = __vmalloc_node_range(size, align,
-                  VMALLOC_START, VMALLOC_END,
-                  GFP_KERNEL | __GFP_ZERO,
-                  PAGE_KERNEL,
-                  0, 0, __builtin_return_address(0));
-
+               ptr = __vmalloc_node(size, align, GFP_KERNEL | __GFP_ZERO, 0,
+                               __builtin_return_address(0));
                if (!ptr)
                        return -1;
 
@@ -118,12 +114,8 @@ static int align_shift_alloc_test(void)
        for (i = 0; i < BITS_PER_LONG; i++) {
                align = ((unsigned long) 1) << i;
 
-               ptr = __vmalloc_node_range(PAGE_SIZE, align,
-                       VMALLOC_START, VMALLOC_END,
-                       GFP_KERNEL | __GFP_ZERO,
-                       PAGE_KERNEL,
-                       0, 0, __builtin_return_address(0));
-
+               ptr = __vmalloc_node(PAGE_SIZE, align, GFP_KERNEL|__GFP_ZERO, 0,
+                               __builtin_return_address(0));
                if (!ptr)
                        return -1;
 
@@ -139,13 +131,9 @@ static int fix_align_alloc_test(void)
        int i;
 
        for (i = 0; i < test_loop_count; i++) {
-               ptr = __vmalloc_node_range(5 * PAGE_SIZE,
-                       THREAD_ALIGN << 1,
-                       VMALLOC_START, VMALLOC_END,
-                       GFP_KERNEL | __GFP_ZERO,
-                       PAGE_KERNEL,
-                       0, 0, __builtin_return_address(0));
-
+               ptr = __vmalloc_node(5 * PAGE_SIZE, THREAD_ALIGN << 1,
+                               GFP_KERNEL | __GFP_ZERO, 0,
+                               __builtin_return_address(0));
                if (!ptr)
                        return -1;
 
index cbb4d9ec00f207a8156cab8a4aa52fb8a49ff43e..ca2a697a206181803531691db196c5475fb57959 100644 (file)
@@ -58,7 +58,7 @@ int check_zeroed_user(const void __user *from, size_t size)
        from -= align;
        size += align;
 
-       if (!user_access_begin(from, size))
+       if (!user_read_access_begin(from, size))
                return -EFAULT;
 
        unsafe_get_user(val, (unsigned long __user *) from, err_fault);
@@ -79,10 +79,10 @@ int check_zeroed_user(const void __user *from, size_t size)
                val &= aligned_byte_mask(size);
 
 done:
-       user_access_end();
+       user_read_access_end();
        return (val == 0);
 err_fault:
-       user_access_end();
+       user_read_access_end();
        return -EFAULT;
 }
 EXPORT_SYMBOL(check_zeroed_user);
index 7c47ad52ce2f7448cda5da17cb2bf25527b0eb12..259e55895933f795aad4238dc72a5c91ab3dbb2d 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/dcache.h>
 #include <linux/cred.h>
 #include <linux/rtc.h>
+#include <linux/time.h>
 #include <linux/uuid.h>
 #include <linux/of.h>
 #include <net/addrconf.h>
@@ -58,7 +59,7 @@
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  *
- * This function is obsolete. Please use kstrtoull instead.
+ * This function has caveats. Please use kstrtoull instead.
  */
 unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
 {
@@ -83,7 +84,7 @@ EXPORT_SYMBOL(simple_strtoull);
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  *
- * This function is obsolete. Please use kstrtoul instead.
+ * This function has caveats. Please use kstrtoul instead.
  */
 unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
 {
@@ -97,7 +98,7 @@ EXPORT_SYMBOL(simple_strtoul);
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  *
- * This function is obsolete. Please use kstrtol instead.
+ * This function has caveats. Please use kstrtol instead.
  */
 long simple_strtol(const char *cp, char **endp, unsigned int base)
 {
@@ -114,7 +115,7 @@ EXPORT_SYMBOL(simple_strtol);
  * @endp: A pointer to the end of the parsed string will be placed here
  * @base: The number base to use
  *
- * This function is obsolete. Please use kstrtoll instead.
+ * This function has caveats. Please use kstrtoll instead.
  */
 long long simple_strtoll(const char *cp, char **endp, unsigned int base)
 {
@@ -1826,6 +1827,29 @@ char *rtc_str(char *buf, char *end, const struct rtc_time *tm,
        return buf;
 }
 
+static noinline_for_stack
+char *time64_str(char *buf, char *end, const time64_t time,
+                struct printf_spec spec, const char *fmt)
+{
+       struct rtc_time rtc_time;
+       struct tm tm;
+
+       time64_to_tm(time, 0, &tm);
+
+       rtc_time.tm_sec = tm.tm_sec;
+       rtc_time.tm_min = tm.tm_min;
+       rtc_time.tm_hour = tm.tm_hour;
+       rtc_time.tm_mday = tm.tm_mday;
+       rtc_time.tm_mon = tm.tm_mon;
+       rtc_time.tm_year = tm.tm_year;
+       rtc_time.tm_wday = tm.tm_wday;
+       rtc_time.tm_yday = tm.tm_yday;
+
+       rtc_time.tm_isdst = 0;
+
+       return rtc_str(buf, end, &rtc_time, spec, fmt);
+}
+
 static noinline_for_stack
 char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
                    const char *fmt)
@@ -1833,8 +1857,10 @@ char *time_and_date(char *buf, char *end, void *ptr, struct printf_spec spec,
        switch (fmt[1]) {
        case 'R':
                return rtc_str(buf, end, (const struct rtc_time *)ptr, spec, fmt);
+       case 'T':
+               return time64_str(buf, end, *(const time64_t *)ptr, spec, fmt);
        default:
-               return error_string(buf, end, "(%ptR?)", spec);
+               return error_string(buf, end, "(%pt?)", spec);
        }
 }
 
@@ -2150,8 +2176,9 @@ char *fwnode_string(char *buf, char *end, struct fwnode_handle *fwnode,
  * - 'd[234]' For a dentry name (optionally 2-4 last components)
  * - 'D[234]' Same as 'd' but for a struct file
  * - 'g' For block_device name (gendisk + partition number)
- * - 't[R][dt][r]' For time and date as represented:
+ * - 't[RT][dt][r]' For time and date as represented by:
  *      R    struct rtc_time
+ *      T    time64_t
  * - 'C' For a clock, it prints the name (Common Clock Framework) or address
  *       (legacy clock framework) of the clock
  * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
index c1acc34c1c358c2fafe1c5fb81517eb2e7e41416..5c0362bd8d56a338c82ea73609d30f7817a2e2b2 100644 (file)
@@ -705,9 +705,9 @@ config ZSMALLOC
          returned by an alloc().  This handle must be mapped in order to
          access the allocated space.
 
-config PGTABLE_MAPPING
+config ZSMALLOC_PGTABLE_MAPPING
        bool "Use page table mapping to access object in zsmalloc"
-       depends on ZSMALLOC
+       depends on ZSMALLOC=y
        help
          By default, zsmalloc uses a copy-based object mapping method to
          access allocations that span two pages. However, if a particular
index 46f0fcc93081eded97384a8c19bdc98bf98759b2..c9d659e6a02c5e0e32925fc0f6983785d4600641 100644 (file)
@@ -2243,15 +2243,11 @@ check_drain:
                 * would succeed.
                 */
                if (cc->order > 0 && last_migrated_pfn) {
-                       int cpu;
                        unsigned long current_block_start =
                                block_start_pfn(cc->migrate_pfn, cc->order);
 
                        if (last_migrated_pfn < current_block_start) {
-                               cpu = get_cpu();
-                               lru_add_drain_cpu(cpu);
-                               drain_local_pages(cc->zone);
-                               put_cpu();
+                               lru_add_drain_cpu_zone(cc->zone);
                                /* No more flushing until we migrate again */
                                last_migrated_pfn = 0;
                        }
index 2189357f098717d1c3c46d2aa8ba061cf310a5f0..f2ede2df585a9640f033224231339199ad576071 100644 (file)
@@ -110,13 +110,57 @@ void __dump_page(struct page *page, const char *reason)
        else if (PageAnon(page))
                type = "anon ";
        else if (mapping) {
-               if (mapping->host && mapping->host->i_dentry.first) {
-                       struct dentry *dentry;
-                       dentry = container_of(mapping->host->i_dentry.first, struct dentry, d_u.d_alias);
-                       pr_warn("%ps name:\"%pd\"\n", mapping->a_ops, dentry);
-               } else
-                       pr_warn("%ps\n", mapping->a_ops);
+               const struct inode *host;
+               const struct address_space_operations *a_ops;
+               const struct hlist_node *dentry_first;
+               const struct dentry *dentry_ptr;
+               struct dentry dentry;
+
+               /*
+                * mapping can be invalid pointer and we don't want to crash
+                * accessing it, so probe everything depending on it carefully
+                */
+               if (probe_kernel_read_strict(&host, &mapping->host,
+                                               sizeof(struct inode *)) ||
+                   probe_kernel_read_strict(&a_ops, &mapping->a_ops,
+                               sizeof(struct address_space_operations *))) {
+                       pr_warn("failed to read mapping->host or a_ops, mapping not a valid kernel address?\n");
+                       goto out_mapping;
+               }
+
+               if (!host) {
+                       pr_warn("mapping->a_ops:%ps\n", a_ops);
+                       goto out_mapping;
+               }
+
+               if (probe_kernel_read_strict(&dentry_first,
+                       &host->i_dentry.first, sizeof(struct hlist_node *))) {
+                       pr_warn("mapping->a_ops:%ps with invalid mapping->host inode address %px\n",
+                               a_ops, host);
+                       goto out_mapping;
+               }
+
+               if (!dentry_first) {
+                       pr_warn("mapping->a_ops:%ps\n", a_ops);
+                       goto out_mapping;
+               }
+
+               dentry_ptr = container_of(dentry_first, struct dentry, d_u.d_alias);
+               if (probe_kernel_read_strict(&dentry, dentry_ptr,
+                                                       sizeof(struct dentry))) {
+                       pr_warn("mapping->aops:%ps with invalid mapping->host->i_dentry.first %px\n",
+                               a_ops, dentry_ptr);
+               } else {
+                       /*
+                        * if dentry is corrupted, the %pd handler may still
+                        * crash, but it's unlikely that we reach here with a
+                        * corrupted struct page
+                        */
+                       pr_warn("mapping->aops:%ps dentry name:\"%pd\"\n",
+                                                               a_ops, &dentry);
+               }
        }
+out_mapping:
        BUILD_BUG_ON(ARRAY_SIZE(pageflag_names) != __NR_PAGEFLAGS + 1);
 
        pr_warn("%sflags: %#lx(%pGp)%s\n", type, page->flags, &page->flags,
index 4f17c83db5758d03bc06e05e38a60357ad593434..0e66f2aaeea3e076dc4ddcfb39cb2aad13709435 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <asm/unistd.h>
 
+#include "internal.h"
+
 /*
  * POSIX_FADV_WILLNEED could set PG_Referenced, and POSIX_FADV_NOREUSE could
  * deactivate the pages and clear PG_Referenced.
@@ -102,10 +104,6 @@ int generic_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
                if (!nrpages)
                        nrpages = ~0UL;
 
-               /*
-                * Ignore return value because fadvise() shall return
-                * success even if filesystem can't retrieve a hint,
-                */
                force_page_cache_readahead(mapping, file, start_index, nrpages);
                break;
        case POSIX_FADV_NOREUSE:
index 23a051a7ef0fb46936d342d7b25b166473428a6e..fe079e9219d1692553c34ddfa40d21577fd5e48a 100644 (file)
@@ -2566,7 +2566,6 @@ page_not_uptodate:
        if (!error || error == AOP_TRUNCATED_PAGE)
                goto retry_find;
 
-       /* Things didn't work out. Return zero to tell the mm layer so. */
        shrink_readahead_size_eio(ra);
        return VM_FAULT_SIGBUS;
 
index 87a6a59fe667706255ddd356956f3ee6d6f19ca2..3edf740a3897c60ba8644699685bc9d06a5416d4 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -382,13 +382,22 @@ static int follow_pfn_pte(struct vm_area_struct *vma, unsigned long address,
 }
 
 /*
- * FOLL_FORCE can write to even unwritable pte's, but only
- * after we've gone through a COW cycle and they are dirty.
+ * FOLL_FORCE or a forced COW break can write even to unwritable pte's,
+ * but only after we've gone through a COW cycle and they are dirty.
  */
 static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
 {
-       return pte_write(pte) ||
-               ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
+       return pte_write(pte) || ((flags & FOLL_COW) && pte_dirty(pte));
+}
+
+/*
+ * A (separate) COW fault might break the page the other way and
+ * get_user_pages() would return the page from what is now the wrong
+ * VM. So we need to force a COW break at GUP time even for reads.
+ */
+static inline bool should_force_cow_break(struct vm_area_struct *vma, unsigned int flags)
+{
+       return is_cow_mapping(vma->vm_flags) && (flags & (FOLL_GET | FOLL_PIN));
 }
 
 static struct page *follow_page_pte(struct vm_area_struct *vma,
@@ -1066,9 +1075,11 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                goto out;
                        }
                        if (is_vm_hugetlb_page(vma)) {
+                               if (should_force_cow_break(vma, foll_flags))
+                                       foll_flags |= FOLL_WRITE;
                                i = follow_hugetlb_page(mm, vma, pages, vmas,
                                                &start, &nr_pages, i,
-                                               gup_flags, locked);
+                                               foll_flags, locked);
                                if (locked && *locked == 0) {
                                        /*
                                         * We've got a VM_FAULT_RETRY
@@ -1082,6 +1093,10 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                                continue;
                        }
                }
+
+               if (should_force_cow_break(vma, foll_flags))
+                       foll_flags |= FOLL_WRITE;
+
 retry:
                /*
                 * If we have a pending SIGKILL, don't keep faulting pages and
@@ -1168,7 +1183,7 @@ static bool vma_permits_fault(struct vm_area_struct *vma,
        return true;
 }
 
-/*
+/**
  * fixup_user_fault() - manually resolve a user page fault
  * @tsk:       the task_struct to use for page fault accounting, or
  *             NULL if faults are not to be recorded.
@@ -1176,7 +1191,8 @@ static bool vma_permits_fault(struct vm_area_struct *vma,
  * @address:   user address
  * @fault_flags:flags to pass down to handle_mm_fault()
  * @unlocked:  did we unlock the mmap_sem while retrying, maybe NULL if caller
- *             does not allow retry
+ *             does not allow retry. If NULL, the caller must guarantee
+ *             that fault_flags does not contain FAULT_FLAG_ALLOW_RETRY.
  *
  * This is meant to be called in the specific scenario where for locking reasons
  * we try to access user memory in atomic context (within a pagefault_disable()
@@ -1839,7 +1855,7 @@ static long __get_user_pages_remote(struct task_struct *tsk,
                                       gup_flags | FOLL_TOUCH | FOLL_REMOTE);
 }
 
-/*
+/**
  * get_user_pages_remote() - pin user pages in memory
  * @tsk:       the task_struct to use for page fault accounting, or
  *             NULL if faults are not to be recorded.
@@ -1870,13 +1886,13 @@ static long __get_user_pages_remote(struct task_struct *tsk,
  *
  * Must be called with mmap_sem held for read or write.
  *
- * get_user_pages walks a process's page tables and takes a reference to
- * each struct page that each user address corresponds to at a given
+ * get_user_pages_remote walks a process's page tables and takes a reference
+ * to each struct page that each user address corresponds to at a given
  * instant. That is, it takes the page that would be accessed if a user
  * thread accesses the given user virtual address at that instant.
  *
  * This does not guarantee that the page exists in the user mappings when
- * get_user_pages returns, and there may even be a completely different
+ * get_user_pages_remote returns, and there may even be a completely different
  * page there in some cases (eg. if mmapped pagecache has been invalidated
  * and subsequently re faulted). However it does guarantee that the page
  * won't be freed completely. And mostly callers simply care that the page
@@ -1888,17 +1904,17 @@ static long __get_user_pages_remote(struct task_struct *tsk,
  * is written to, set_page_dirty (or set_page_dirty_lock, as appropriate) must
  * be called after the page is finished with, and before put_page is called.
  *
- * get_user_pages is typically used for fewer-copy IO operations, to get a
- * handle on the memory by some means other than accesses via the user virtual
- * addresses. The pages may be submitted for DMA to devices or accessed via
- * their kernel linear mapping (via the kmap APIs). Care should be taken to
- * use the correct cache flushing APIs.
+ * get_user_pages_remote is typically used for fewer-copy IO operations,
+ * to get a handle on the memory by some means other than accesses
+ * via the user virtual addresses. The pages may be submitted for
+ * DMA to devices or accessed via their kernel linear mapping (via the
+ * kmap APIs). Care should be taken to use the correct cache flushing APIs.
  *
  * See also get_user_pages_fast, for performance critical applications.
  *
- * get_user_pages should be phased out in favor of
+ * get_user_pages_remote should be phased out in favor of
  * get_user_pages_locked|unlocked or get_user_pages_fast. Nothing
- * should use get_user_pages because it cannot pass
+ * should use get_user_pages_remote because it cannot pass
  * FAULT_FLAG_ALLOW_RETRY to handle_mm_fault.
  */
 long get_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
@@ -1937,7 +1953,17 @@ static long __get_user_pages_remote(struct task_struct *tsk,
 }
 #endif /* !CONFIG_MMU */
 
-/*
+/**
+ * get_user_pages() - pin user pages in memory
+ * @start:      starting user address
+ * @nr_pages:   number of pages from start to pin
+ * @gup_flags:  flags modifying lookup behaviour
+ * @pages:      array that receives pointers to the pages pinned.
+ *              Should be at least nr_pages long. Or NULL, if caller
+ *              only intends to ensure the pages are faulted in.
+ * @vmas:       array of pointers to vmas corresponding to each page.
+ *              Or NULL if the caller does not require them.
+ *
  * This is the same as get_user_pages_remote(), just with a
  * less-flexible calling convention where we assume that the task
  * and mm being operated on are the current task's and don't allow
@@ -1960,11 +1986,7 @@ long get_user_pages(unsigned long start, unsigned long nr_pages,
 }
 EXPORT_SYMBOL(get_user_pages);
 
-/*
- * We can leverage the VM_FAULT_RETRY functionality in the page fault
- * paths better by using either get_user_pages_locked() or
- * get_user_pages_unlocked().
- *
+/**
  * get_user_pages_locked() is suitable to replace the form:
  *
  *      down_read(&mm->mmap_sem);
@@ -1980,6 +2002,21 @@ EXPORT_SYMBOL(get_user_pages);
  *      get_user_pages_locked(tsk, mm, ..., pages, &locked);
  *      if (locked)
  *          up_read(&mm->mmap_sem);
+ *
+ * @start:      starting user address
+ * @nr_pages:   number of pages from start to pin
+ * @gup_flags:  flags modifying lookup behaviour
+ * @pages:      array that receives pointers to the pages pinned.
+ *              Should be at least nr_pages long. Or NULL, if caller
+ *              only intends to ensure the pages are faulted in.
+ * @locked:     pointer to lock flag indicating whether lock is held and
+ *              subsequently whether VM_FAULT_RETRY functionality can be
+ *              utilised. Lock must initially be held.
+ *
+ * We can leverage the VM_FAULT_RETRY functionality in the page fault
+ * paths better by using either get_user_pages_locked() or
+ * get_user_pages_unlocked().
+ *
  */
 long get_user_pages_locked(unsigned long start, unsigned long nr_pages,
                           unsigned int gup_flags, struct page **pages,
@@ -2674,6 +2711,10 @@ static bool gup_fast_permitted(unsigned long start, unsigned long end)
  *
  * If the architecture does not support this function, simply return with no
  * pages pinned.
+ *
+ * Careful, careful! COW breaking can go either way, so a non-write
+ * access can get ambiguous page results. If you call this function without
+ * 'write' set, you'd better be sure that you're ok with that ambiguity.
  */
 int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
                          struct page **pages)
@@ -2709,6 +2750,12 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
         *
         * We do not adopt an rcu_read_lock(.) here as we also want to
         * block IPIs that come from THPs splitting.
+        *
+        * NOTE! We allow read-only gup_fast() here, but you'd better be
+        * careful about possible COW pages. You'll get _a_ COW page, but
+        * not necessarily the one you intended to get depending on what
+        * COW event happens after this. COW may break the page copy in a
+        * random direction.
         */
 
        if (IS_ENABLED(CONFIG_HAVE_FAST_GUP) &&
@@ -2766,10 +2813,17 @@ static int internal_get_user_pages_fast(unsigned long start, int nr_pages,
        if (unlikely(!access_ok((void __user *)start, len)))
                return -EFAULT;
 
+       /*
+        * The FAST_GUP case requires FOLL_WRITE even for pure reads,
+        * because get_user_pages() may need to cause an early COW in
+        * order to avoid confusing the normal COW routines. So only
+        * targets that are already writable are safe to do by just
+        * looking at the page tables.
+        */
        if (IS_ENABLED(CONFIG_HAVE_FAST_GUP) &&
            gup_fast_permitted(start, end)) {
                local_irq_disable();
-               gup_pgd_range(addr, end, gup_flags, pages, &nr_pinned);
+               gup_pgd_range(addr, end, gup_flags | FOLL_WRITE, pages, &nr_pinned);
                local_irq_enable();
                ret = nr_pinned;
        }
@@ -2845,9 +2899,9 @@ EXPORT_SYMBOL_GPL(get_user_pages_fast);
  * the arguments here are identical.
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
- * see Documentation/vm/pin_user_pages.rst for further details.
+ * see Documentation/core-api/pin_user_pages.rst for further details.
  *
- * This is intended for Case 1 (DIO) in Documentation/vm/pin_user_pages.rst. It
+ * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
  * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 int pin_user_pages_fast(unsigned long start, int nr_pages,
@@ -2885,9 +2939,9 @@ EXPORT_SYMBOL_GPL(pin_user_pages_fast);
  * the arguments here are identical.
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
- * see Documentation/vm/pin_user_pages.rst for details.
+ * see Documentation/core-api/pin_user_pages.rst for details.
  *
- * This is intended for Case 1 (DIO) in Documentation/vm/pin_user_pages.rst. It
+ * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
  * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 long pin_user_pages_remote(struct task_struct *tsk, struct mm_struct *mm,
@@ -2921,9 +2975,9 @@ EXPORT_SYMBOL(pin_user_pages_remote);
  * FOLL_PIN is set.
  *
  * FOLL_PIN means that the pages must be released via unpin_user_page(). Please
- * see Documentation/vm/pin_user_pages.rst for details.
+ * see Documentation/core-api/pin_user_pages.rst for details.
  *
- * This is intended for Case 1 (DIO) in Documentation/vm/pin_user_pages.rst. It
+ * This is intended for Case 1 (DIO) in Documentation/core-api/pin_user_pages.rst. It
  * is NOT intended for Case 2 (RDMA: long-term pins).
  */
 long pin_user_pages(unsigned long start, unsigned long nr_pages,
@@ -2939,3 +2993,20 @@ long pin_user_pages(unsigned long start, unsigned long nr_pages,
                                     pages, vmas, gup_flags);
 }
 EXPORT_SYMBOL(pin_user_pages);
+
+/*
+ * pin_user_pages_unlocked() is the FOLL_PIN variant of
+ * get_user_pages_unlocked(). Behavior is the same, except that this one sets
+ * FOLL_PIN and rejects FOLL_GET.
+ */
+long pin_user_pages_unlocked(unsigned long start, unsigned long nr_pages,
+                            struct page **pages, unsigned int gup_flags)
+{
+       /* FOLL_GET and FOLL_PIN are mutually exclusive. */
+       if (WARN_ON_ONCE(gup_flags & FOLL_GET))
+               return -EINVAL;
+
+       gup_flags |= FOLL_PIN;
+       return get_user_pages_unlocked(start, nr_pages, pages, gup_flags);
+}
+EXPORT_SYMBOL(pin_user_pages_unlocked);
index 6ecd1045113b538586e87a00aec4022cf500c1f1..11fe0b4dbe6730e0bdd9c5cb987c4d93feb45df7 100644 (file)
@@ -1515,13 +1515,12 @@ out_unlock:
 }
 
 /*
- * FOLL_FORCE can write to even unwritable pmd's, but only
- * after we've gone through a COW cycle and they are dirty.
+ * FOLL_FORCE or a forced COW break can write even to unwritable pmd's,
+ * but only after we've gone through a COW cycle and they are dirty.
  */
 static inline bool can_follow_write_pmd(pmd_t pmd, unsigned int flags)
 {
-       return pmd_write(pmd) ||
-              ((flags & FOLL_FORCE) && (flags & FOLL_COW) && pmd_dirty(pmd));
+       return pmd_write(pmd) || ((flags & FOLL_COW) && pmd_dirty(pmd));
 }
 
 struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
index b5634e78f01dc6aa49b0c6926ba444b69c5fcadc..f762a34b0c570ff0db963ef6b077223e83c55f6a 100644 (file)
@@ -49,18 +49,20 @@ void unmap_page_range(struct mmu_gather *tlb,
                             unsigned long addr, unsigned long end,
                             struct zap_details *details);
 
-extern unsigned int __do_page_cache_readahead(struct address_space *mapping,
-               struct file *filp, pgoff_t offset, unsigned long nr_to_read,
+void force_page_cache_readahead(struct address_space *, struct file *,
+               pgoff_t index, unsigned long nr_to_read);
+void __do_page_cache_readahead(struct address_space *, struct file *,
+               pgoff_t index, unsigned long nr_to_read,
                unsigned long lookahead_size);
 
 /*
  * Submit IO for the read-ahead request in file_ra_state.
  */
-static inline unsigned long ra_submit(struct file_ra_state *ra,
+static inline void ra_submit(struct file_ra_state *ra,
                struct address_space *mapping, struct file *filp)
 {
-       return __do_page_cache_readahead(mapping, filp,
-                                       ra->start, ra->size, ra->async_size);
+       __do_page_cache_readahead(mapping, filp,
+                       ra->start, ra->size, ra->async_size);
 }
 
 /**
index de3121848ddf3e7d053006c0edd0b1c00a5ebdaf..d532c25877311797c5936f096f920adaec979b7e 100644 (file)
@@ -15,14 +15,19 @@ CFLAGS_REMOVE_tags_report.o = $(CC_FLAGS_FTRACE)
 
 # Function splitter causes unnecessary splits in __asan_load1/__asan_store1
 # see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63533
-CFLAGS_common.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_generic.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_generic_report.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_init.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_quarantine.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_report.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_tags.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
-CFLAGS_tags_report.o := $(call cc-option, -fno-conserve-stack -fno-stack-protector) -DDISABLE_BRANCH_PROFILING
+CC_FLAGS_KASAN_RUNTIME := $(call cc-option, -fno-conserve-stack)
+CC_FLAGS_KASAN_RUNTIME += $(call cc-option, -fno-stack-protector)
+# Disable branch tracing to avoid recursion.
+CC_FLAGS_KASAN_RUNTIME += -DDISABLE_BRANCH_PROFILING
+
+CFLAGS_common.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_generic_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_init.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_quarantine.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_tags.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_tags_report.o := $(CC_FLAGS_KASAN_RUNTIME)
 
 obj-$(CONFIG_KASAN) := common.o init.o report.o
 obj-$(CONFIG_KASAN_GENERIC) += generic.o generic_report.o quarantine.o
index 2906358e42f05287731deb8f2c7e86ddbc5b4a59..757d4074fe286a4416690a5919e7d93227d0aea0 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/types.h>
 #include <linux/vmalloc.h>
 #include <linux/bug.h>
-#include <linux/uaccess.h>
 
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
@@ -613,24 +612,6 @@ void kasan_free_shadow(const struct vm_struct *vm)
 }
 #endif
 
-extern void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip);
-extern bool report_enabled(void);
-
-bool kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip)
-{
-       unsigned long flags = user_access_save();
-       bool ret = false;
-
-       if (likely(report_enabled())) {
-               __kasan_report(addr, size, is_write, ip);
-               ret = true;
-       }
-
-       user_access_restore(flags);
-
-       return ret;
-}
-
 #ifdef CONFIG_MEMORY_HOTPLUG
 static bool shadow_mapped(unsigned long addr)
 {
index 80f23c9da6b08062f32f668ede36b81a50ba4c95..51ec45407a0b95805b5fce85e20295edb9d7de7f 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kasan.h>
 #include <linux/module.h>
 #include <linux/sched/task_stack.h>
+#include <linux/uaccess.h>
 
 #include <asm/sections.h>
 
@@ -454,7 +455,7 @@ static void print_shadow_for_address(const void *addr)
        }
 }
 
-bool report_enabled(void)
+static bool report_enabled(void)
 {
        if (current->kasan_depth)
                return false;
@@ -479,7 +480,8 @@ void kasan_report_invalid_free(void *object, unsigned long ip)
        end_report(&flags);
 }
 
-void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip)
+static void __kasan_report(unsigned long addr, size_t size, bool is_write,
+                               unsigned long ip)
 {
        struct kasan_access_info info;
        void *tagged_addr;
@@ -518,6 +520,22 @@ void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned lon
        end_report(&flags);
 }
 
+bool kasan_report(unsigned long addr, size_t size, bool is_write,
+                       unsigned long ip)
+{
+       unsigned long flags = user_access_save();
+       bool ret = false;
+
+       if (likely(report_enabled())) {
+               __kasan_report(addr, size, is_write, ip);
+               ret = true;
+       }
+
+       user_access_restore(flags);
+
+       return ret;
+}
+
 #ifdef CONFIG_KASAN_INLINE
 /*
  * With CONFIG_KASAN_INLINE, accesses to bogus pointers (outside the high
index 99d77ffb79c2b39b2cd1cc4dfd72c4bf1684f3d6..cd280afb246e2cd7ad61bbd06072fb9ed8db8517 100644 (file)
@@ -1692,6 +1692,7 @@ static void collapse_file(struct mm_struct *mm,
                if (page_has_private(page) &&
                    !try_to_release_page(page, GFP_KERNEL)) {
                        result = SCAN_PAGE_HAS_PRIVATE;
+                       putback_lru_page(page);
                        goto out_unlock;
                }
 
index a3b97f10396654b18faa4642dafc4975507da45b..f973a025569bc6d659420b4f737f114160fe40d8 100644 (file)
@@ -1314,7 +1314,7 @@ static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)
        if (do_memsw_account()) {
                count = page_counter_read(&memcg->memsw);
                limit = READ_ONCE(memcg->memsw.max);
-               if (count <= limit)
+               if (count < limit)
                        margin = min(margin, limit - count);
                else
                        margin = 0;
@@ -1451,6 +1451,8 @@ static char *memory_stat_format(struct mem_cgroup *memcg)
                       memcg_page_state(memcg, WORKINGSET_REFAULT));
        seq_buf_printf(&s, "workingset_activate %lu\n",
                       memcg_page_state(memcg, WORKINGSET_ACTIVATE));
+       seq_buf_printf(&s, "workingset_restore %lu\n",
+                      memcg_page_state(memcg, WORKINGSET_RESTORE));
        seq_buf_printf(&s, "workingset_nodereclaim %lu\n",
                       memcg_page_state(memcg, WORKINGSET_NODERECLAIM));
 
@@ -2250,7 +2252,8 @@ static void reclaim_high(struct mem_cgroup *memcg,
                         gfp_t gfp_mask)
 {
        do {
-               if (page_counter_read(&memcg->memory) <= READ_ONCE(memcg->high))
+               if (page_counter_read(&memcg->memory) <=
+                   READ_ONCE(memcg->memory.high))
                        continue;
                memcg_memory_event(memcg, MEMCG_HIGH);
                try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true);
@@ -2319,41 +2322,64 @@ static void high_work_func(struct work_struct *work)
  #define MEMCG_DELAY_PRECISION_SHIFT 20
  #define MEMCG_DELAY_SCALING_SHIFT 14
 
-/*
- * Get the number of jiffies that we should penalise a mischievous cgroup which
- * is exceeding its memory.high by checking both it and its ancestors.
- */
-static unsigned long calculate_high_delay(struct mem_cgroup *memcg,
-                                         unsigned int nr_pages)
+static u64 calculate_overage(unsigned long usage, unsigned long high)
 {
-       unsigned long penalty_jiffies;
-       u64 max_overage = 0;
+       u64 overage;
 
-       do {
-               unsigned long usage, high;
-               u64 overage;
+       if (usage <= high)
+               return 0;
 
-               usage = page_counter_read(&memcg->memory);
-               high = READ_ONCE(memcg->high);
+       /*
+        * Prevent division by 0 in overage calculation by acting as if
+        * it was a threshold of 1 page
+        */
+       high = max(high, 1UL);
 
-               if (usage <= high)
-                       continue;
+       overage = usage - high;
+       overage <<= MEMCG_DELAY_PRECISION_SHIFT;
+       return div64_u64(overage, high);
+}
 
-               /*
-                * Prevent division by 0 in overage calculation by acting as if
-                * it was a threshold of 1 page
-                */
-               high = max(high, 1UL);
+static u64 mem_find_max_overage(struct mem_cgroup *memcg)
+{
+       u64 overage, max_overage = 0;
+
+       do {
+               overage = calculate_overage(page_counter_read(&memcg->memory),
+                                           READ_ONCE(memcg->memory.high));
+               max_overage = max(overage, max_overage);
+       } while ((memcg = parent_mem_cgroup(memcg)) &&
+                !mem_cgroup_is_root(memcg));
 
-               overage = usage - high;
-               overage <<= MEMCG_DELAY_PRECISION_SHIFT;
-               overage = div64_u64(overage, high);
+       return max_overage;
+}
 
-               if (overage > max_overage)
-                       max_overage = overage;
+static u64 swap_find_max_overage(struct mem_cgroup *memcg)
+{
+       u64 overage, max_overage = 0;
+
+       do {
+               overage = calculate_overage(page_counter_read(&memcg->swap),
+                                           READ_ONCE(memcg->swap.high));
+               if (overage)
+                       memcg_memory_event(memcg, MEMCG_SWAP_HIGH);
+               max_overage = max(overage, max_overage);
        } while ((memcg = parent_mem_cgroup(memcg)) &&
                 !mem_cgroup_is_root(memcg));
 
+       return max_overage;
+}
+
+/*
+ * Get the number of jiffies that we should penalise a mischievous cgroup which
+ * is exceeding its memory.high by checking both it and its ancestors.
+ */
+static unsigned long calculate_high_delay(struct mem_cgroup *memcg,
+                                         unsigned int nr_pages,
+                                         u64 max_overage)
+{
+       unsigned long penalty_jiffies;
+
        if (!max_overage)
                return 0;
 
@@ -2377,14 +2403,7 @@ static unsigned long calculate_high_delay(struct mem_cgroup *memcg,
         * MEMCG_CHARGE_BATCH pages is nominal, so work out how much smaller or
         * larger the current charge patch is than that.
         */
-       penalty_jiffies = penalty_jiffies * nr_pages / MEMCG_CHARGE_BATCH;
-
-       /*
-        * Clamp the max delay per usermode return so as to still keep the
-        * application moving forwards and also permit diagnostics, albeit
-        * extremely slowly.
-        */
-       return min(penalty_jiffies, MEMCG_MAX_HIGH_DELAY_JIFFIES);
+       return penalty_jiffies * nr_pages / MEMCG_CHARGE_BATCH;
 }
 
 /*
@@ -2409,7 +2428,18 @@ void mem_cgroup_handle_over_high(void)
         * memory.high is breached and reclaim is unable to keep up. Throttle
         * allocators proactively to slow down excessive growth.
         */
-       penalty_jiffies = calculate_high_delay(memcg, nr_pages);
+       penalty_jiffies = calculate_high_delay(memcg, nr_pages,
+                                              mem_find_max_overage(memcg));
+
+       penalty_jiffies += calculate_high_delay(memcg, nr_pages,
+                                               swap_find_max_overage(memcg));
+
+       /*
+        * Clamp the max delay per usermode return so as to still keep the
+        * application moving forwards and also permit diagnostics, albeit
+        * extremely slowly.
+        */
+       penalty_jiffies = min(penalty_jiffies, MEMCG_MAX_HIGH_DELAY_JIFFIES);
 
        /*
         * Don't sleep if the amount of jiffies this memcg owes us is so low
@@ -2594,12 +2624,32 @@ done_restock:
         * reclaim, the cost of mismatch is negligible.
         */
        do {
-               if (page_counter_read(&memcg->memory) > READ_ONCE(memcg->high)) {
-                       /* Don't bother a random interrupted task */
-                       if (in_interrupt()) {
+               bool mem_high, swap_high;
+
+               mem_high = page_counter_read(&memcg->memory) >
+                       READ_ONCE(memcg->memory.high);
+               swap_high = page_counter_read(&memcg->swap) >
+                       READ_ONCE(memcg->swap.high);
+
+               /* Don't bother a random interrupted task */
+               if (in_interrupt()) {
+                       if (mem_high) {
                                schedule_work(&memcg->high_work);
                                break;
                        }
+                       continue;
+               }
+
+               if (mem_high || swap_high) {
+                       /*
+                        * The allocating tasks in this cgroup will need to do
+                        * reclaim or be throttled to prevent further growth
+                        * of the memory or swap footprints.
+                        *
+                        * Target some best-effort fairness between the tasks,
+                        * and distribute reclaim work and delay penalties
+                        * based on how much each task is actually allocating.
+                        */
                        current->memcg_nr_pages_over_high += batch;
                        set_notify_resume(current);
                        break;
@@ -2802,7 +2852,12 @@ static void memcg_schedule_kmem_cache_create(struct mem_cgroup *memcg,
 
 static inline bool memcg_kmem_bypass(void)
 {
-       if (in_interrupt() || !current->mm || (current->flags & PF_KTHREAD))
+       if (in_interrupt())
+               return true;
+
+       /* Allow remote memcg charging in kthread contexts. */
+       if ((!current->mm || (current->flags & PF_KTHREAD)) &&
+            !current->active_memcg)
                return true;
        return false;
 }
@@ -4330,7 +4385,6 @@ void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages,
 
        *pdirty = memcg_exact_page_state(memcg, NR_FILE_DIRTY);
 
-       /* this should eventually include NR_UNSTABLE_NFS */
        *pwriteback = memcg_exact_page_state(memcg, NR_WRITEBACK);
        *pfilepages = memcg_exact_page_state(memcg, NR_INACTIVE_FILE) +
                        memcg_exact_page_state(memcg, NR_ACTIVE_FILE);
@@ -4338,7 +4392,7 @@ void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages,
 
        while ((parent = parent_mem_cgroup(memcg))) {
                unsigned long ceiling = min(READ_ONCE(memcg->memory.max),
-                                           READ_ONCE(memcg->high));
+                                           READ_ONCE(memcg->memory.high));
                unsigned long used = page_counter_read(&memcg->memory);
 
                *pheadroom = min(*pheadroom, ceiling - min(ceiling, used));
@@ -5063,8 +5117,9 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
        if (IS_ERR(memcg))
                return ERR_CAST(memcg);
 
-       WRITE_ONCE(memcg->high, PAGE_COUNTER_MAX);
+       page_counter_set_high(&memcg->memory, PAGE_COUNTER_MAX);
        memcg->soft_limit = PAGE_COUNTER_MAX;
+       page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
        if (parent) {
                memcg->swappiness = mem_cgroup_swappiness(parent);
                memcg->oom_kill_disable = parent->oom_kill_disable;
@@ -5216,8 +5271,9 @@ static void mem_cgroup_css_reset(struct cgroup_subsys_state *css)
        page_counter_set_max(&memcg->tcpmem, PAGE_COUNTER_MAX);
        page_counter_set_min(&memcg->memory, 0);
        page_counter_set_low(&memcg->memory, 0);
-       WRITE_ONCE(memcg->high, PAGE_COUNTER_MAX);
+       page_counter_set_high(&memcg->memory, PAGE_COUNTER_MAX);
        memcg->soft_limit = PAGE_COUNTER_MAX;
+       page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
        memcg_wb_domain_size_changed(memcg);
 }
 
@@ -6015,7 +6071,8 @@ static ssize_t memory_low_write(struct kernfs_open_file *of,
 
 static int memory_high_show(struct seq_file *m, void *v)
 {
-       return seq_puts_memcg_tunable(m, READ_ONCE(mem_cgroup_from_seq(m)->high));
+       return seq_puts_memcg_tunable(m,
+               READ_ONCE(mem_cgroup_from_seq(m)->memory.high));
 }
 
 static ssize_t memory_high_write(struct kernfs_open_file *of,
@@ -6032,7 +6089,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
        if (err)
                return err;
 
-       WRITE_ONCE(memcg->high, high);
+       page_counter_set_high(&memcg->memory, high);
 
        for (;;) {
                unsigned long nr_pages = page_counter_read(&memcg->memory);
@@ -6227,7 +6284,6 @@ static struct cftype memory_files[] = {
        },
        {
                .name = "stat",
-               .flags = CFTYPE_NOT_ON_ROOT,
                .seq_show = memory_stat_show,
        },
        {
@@ -7131,10 +7187,13 @@ bool mem_cgroup_swap_full(struct page *page)
        if (!memcg)
                return false;
 
-       for (; memcg != root_mem_cgroup; memcg = parent_mem_cgroup(memcg))
-               if (page_counter_read(&memcg->swap) * 2 >=
-                   READ_ONCE(memcg->swap.max))
+       for (; memcg != root_mem_cgroup; memcg = parent_mem_cgroup(memcg)) {
+               unsigned long usage = page_counter_read(&memcg->swap);
+
+               if (usage * 2 >= READ_ONCE(memcg->swap.high) ||
+                   usage * 2 >= READ_ONCE(memcg->swap.max))
                        return true;
+       }
 
        return false;
 }
@@ -7164,6 +7223,29 @@ static u64 swap_current_read(struct cgroup_subsys_state *css,
        return (u64)page_counter_read(&memcg->swap) * PAGE_SIZE;
 }
 
+static int swap_high_show(struct seq_file *m, void *v)
+{
+       return seq_puts_memcg_tunable(m,
+               READ_ONCE(mem_cgroup_from_seq(m)->swap.high));
+}
+
+static ssize_t swap_high_write(struct kernfs_open_file *of,
+                              char *buf, size_t nbytes, loff_t off)
+{
+       struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
+       unsigned long high;
+       int err;
+
+       buf = strstrip(buf);
+       err = page_counter_memparse(buf, "max", &high);
+       if (err)
+               return err;
+
+       page_counter_set_high(&memcg->swap, high);
+
+       return nbytes;
+}
+
 static int swap_max_show(struct seq_file *m, void *v)
 {
        return seq_puts_memcg_tunable(m,
@@ -7191,6 +7273,8 @@ static int swap_events_show(struct seq_file *m, void *v)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
 
+       seq_printf(m, "high %lu\n",
+                  atomic_long_read(&memcg->memory_events[MEMCG_SWAP_HIGH]));
        seq_printf(m, "max %lu\n",
                   atomic_long_read(&memcg->memory_events[MEMCG_SWAP_MAX]));
        seq_printf(m, "fail %lu\n",
@@ -7205,6 +7289,12 @@ static struct cftype swap_files[] = {
                .flags = CFTYPE_NOT_ON_ROOT,
                .read_u64 = swap_current_read,
        },
+       {
+               .name = "swap.high",
+               .flags = CFTYPE_NOT_ON_ROOT,
+               .seq_show = swap_high_show,
+               .write = swap_high_write,
+       },
        {
                .name = "swap.max",
                .flags = CFTYPE_NOT_ON_ROOT,
index c4afb407bf0f229c7e9d89668fcb90d0cf429794..ababa368cb68084537d2176c0966f3d6598f5f9c 100644 (file)
@@ -210,14 +210,17 @@ static int kill_proc(struct to_kill *tk, unsigned long pfn, int flags)
 {
        struct task_struct *t = tk->tsk;
        short addr_lsb = tk->size_shift;
-       int ret;
+       int ret = 0;
 
-       pr_err("Memory failure: %#lx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
-               pfn, t->comm, t->pid);
+       if ((t->mm == current->mm) || !(flags & MF_ACTION_REQUIRED))
+               pr_err("Memory failure: %#lx: Sending SIGBUS to %s:%d due to hardware memory corruption\n",
+                       pfn, t->comm, t->pid);
 
-       if ((flags & MF_ACTION_REQUIRED) && t->mm == current->mm) {
-               ret = force_sig_mceerr(BUS_MCEERR_AR, (void __user *)tk->addr,
-                                      addr_lsb);
+       if (flags & MF_ACTION_REQUIRED) {
+               if (t->mm == current->mm)
+                       ret = force_sig_mceerr(BUS_MCEERR_AR,
+                                        (void __user *)tk->addr, addr_lsb);
+               /* send no signal to non-current processes */
        } else {
                /*
                 * Don't use force here, it's convenient if the signal
index f703fe8c83460202354ca1c8cc693eb99b682c2e..21438278afca1213ba89e244aafba1cbc45e2123 100644 (file)
@@ -802,8 +802,6 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                get_page(page);
                page_dup_rmap(page, false);
                rss[mm_counter(page)]++;
-       } else if (pte_devmap(pte)) {
-               page = pte_page(pte);
        }
 
 out_set_pte:
index 7160c1556f797fa961b77065e47e57e628ebf3de..fb425d86c1151491a12fff4cb140a1aa6702fb34 100644 (file)
@@ -797,10 +797,7 @@ recheck_buffers:
        if (rc != MIGRATEPAGE_SUCCESS)
                goto unlock_buffers;
 
-       ClearPagePrivate(page);
-       set_page_private(newpage, page_private(page));
-       set_page_private(page, 0);
-       put_page(page);
+       attach_page_private(newpage, detach_page_private(page));
        get_page(newpage);
 
        bh = head;
@@ -810,8 +807,6 @@ recheck_buffers:
 
        } while (bh != head);
 
-       SetPagePrivate(newpage);
-
        if (mode != MIGRATE_SYNC_NO_COPY)
                migrate_page_copy(newpage, page);
        else
@@ -1032,7 +1027,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
                 * to the LRU. Later, when the IO completes the pages are
                 * marked uptodate and unlocked. However, the queueing
                 * could be merging multiple pages for one bio (e.g.
-                * mpage_readpages). If an allocation happens for the
+                * mpage_readahead). If an allocation happens for the
                 * second or third page, the process can end up locking
                 * the same page twice and deadlocking. Rather than
                 * trying to be clever about what pages can be locked,
index 7da6991d94359cebc28a55949b69802593451495..435e5f794b3ba83721e51edb62b5b53d2a98d408 100644 (file)
@@ -67,26 +67,30 @@ void __init mminit_verify_pageflags_layout(void)
        unsigned long or_mask, add_mask;
 
        shift = 8 * sizeof(unsigned long);
-       width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH - LAST_CPUPID_SHIFT;
+       width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH
+               - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH;
        mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths",
-               "Section %d Node %d Zone %d Lastcpupid %d Flags %d\n",
+               "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n",
                SECTIONS_WIDTH,
                NODES_WIDTH,
                ZONES_WIDTH,
                LAST_CPUPID_WIDTH,
+               KASAN_TAG_WIDTH,
                NR_PAGEFLAGS);
        mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts",
-               "Section %d Node %d Zone %d Lastcpupid %d\n",
+               "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n",
                SECTIONS_SHIFT,
                NODES_SHIFT,
                ZONES_SHIFT,
-               LAST_CPUPID_SHIFT);
+               LAST_CPUPID_SHIFT,
+               KASAN_TAG_WIDTH);
        mminit_dprintk(MMINIT_TRACE, "pageflags_layout_pgshifts",
-               "Section %lu Node %lu Zone %lu Lastcpupid %lu\n",
+               "Section %lu Node %lu Zone %lu Lastcpupid %lu Kasantag %lu\n",
                (unsigned long)SECTIONS_PGSHIFT,
                (unsigned long)NODES_PGSHIFT,
                (unsigned long)ZONES_PGSHIFT,
-               (unsigned long)LAST_CPUPID_PGSHIFT);
+               (unsigned long)LAST_CPUPID_PGSHIFT,
+               (unsigned long)KASAN_TAG_PGSHIFT);
        mminit_dprintk(MMINIT_TRACE, "pageflags_layout_nodezoneid",
                "Node/Zone ID: %lu -> %lu\n",
                (unsigned long)(ZONEID_PGOFF + ZONEID_SHIFT),
index 318df4e236c99003e81880e8619c111a3138dcdf..dfae55f41901374d3aa1db9a70ba415e237d1293 100644 (file)
@@ -140,7 +140,7 @@ void vfree(const void *addr)
 }
 EXPORT_SYMBOL(vfree);
 
-void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
+void *__vmalloc(unsigned long size, gfp_t gfp_mask)
 {
        /*
         *  You can't specify __GFP_HIGHMEM with kmalloc() since kmalloc()
@@ -150,16 +150,25 @@ void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
 }
 EXPORT_SYMBOL(__vmalloc);
 
-void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags)
+void *__vmalloc_node_range(unsigned long size, unsigned long align,
+               unsigned long start, unsigned long end, gfp_t gfp_mask,
+               pgprot_t prot, unsigned long vm_flags, int node,
+               const void *caller)
 {
-       return __vmalloc(size, flags, PAGE_KERNEL);
+       return __vmalloc(size, gfp_mask);
+}
+
+void *__vmalloc_node(unsigned long size, unsigned long align, gfp_t gfp_mask,
+               int node, const void *caller)
+{
+       return __vmalloc(size, gfp_mask);
 }
 
 static void *__vmalloc_user_flags(unsigned long size, gfp_t flags)
 {
        void *ret;
 
-       ret = __vmalloc(size, flags, PAGE_KERNEL);
+       ret = __vmalloc(size, flags);
        if (ret) {
                struct vm_area_struct *vma;
 
@@ -179,12 +188,6 @@ void *vmalloc_user(unsigned long size)
 }
 EXPORT_SYMBOL(vmalloc_user);
 
-void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags)
-{
-       return __vmalloc_user_flags(size, flags | __GFP_ZERO);
-}
-EXPORT_SYMBOL(vmalloc_user_node_flags);
-
 struct page *vmalloc_to_page(const void *addr)
 {
        return virt_to_page(addr);
@@ -230,7 +233,7 @@ long vwrite(char *buf, char *addr, unsigned long count)
  */
 void *vmalloc(unsigned long size)
 {
-       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
+       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM);
 }
 EXPORT_SYMBOL(vmalloc);
 
@@ -248,8 +251,7 @@ EXPORT_SYMBOL(vmalloc);
  */
 void *vzalloc(unsigned long size)
 {
-       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
-                       PAGE_KERNEL);
+       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
 }
 EXPORT_SYMBOL(vzalloc);
 
@@ -302,7 +304,7 @@ EXPORT_SYMBOL(vzalloc_node);
 
 void *vmalloc_exec(unsigned long size)
 {
-       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC);
+       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM);
 }
 
 /**
@@ -314,7 +316,7 @@ void *vmalloc_exec(unsigned long size)
  */
 void *vmalloc_32(unsigned long size)
 {
-       return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL);
+       return __vmalloc(size, GFP_KERNEL);
 }
 EXPORT_SYMBOL(vmalloc_32);
 
@@ -351,7 +353,7 @@ void vunmap(const void *addr)
 }
 EXPORT_SYMBOL(vunmap);
 
-void *vm_map_ram(struct page **pages, unsigned int count, int node, pgprot_t prot)
+void *vm_map_ram(struct page **pages, unsigned int count, int node)
 {
        BUG();
        return NULL;
@@ -369,18 +371,6 @@ void vm_unmap_aliases(void)
 }
 EXPORT_SYMBOL_GPL(vm_unmap_aliases);
 
-/*
- * Implement a stub for vmalloc_sync_[un]mapping() if the architecture
- * chose not to have one.
- */
-void __weak vmalloc_sync_mappings(void)
-{
-}
-
-void __weak vmalloc_sync_unmappings(void)
-{
-}
-
 struct vm_struct *alloc_vm_area(size_t size, pte_t **ptes)
 {
        BUG();
index 7326b54ab728cfd53dd185efb239e5252a84a08a..7185652662577d103d5d275484cf4d485dfbb9f3 100644 (file)
@@ -387,8 +387,7 @@ static unsigned long global_dirtyable_memory(void)
  * Calculate @dtc->thresh and ->bg_thresh considering
  * vm_dirty_{bytes|ratio} and dirty_background_{bytes|ratio}.  The caller
  * must ensure that @dtc->avail is set before calling this function.  The
- * dirty limits will be lifted by 1/4 for PF_LESS_THROTTLE (ie. nfsd) and
- * real-time tasks.
+ * dirty limits will be lifted by 1/4 for real-time tasks.
  */
 static void domain_dirty_limits(struct dirty_throttle_control *dtc)
 {
@@ -436,7 +435,7 @@ static void domain_dirty_limits(struct dirty_throttle_control *dtc)
        if (bg_thresh >= thresh)
                bg_thresh = thresh / 2;
        tsk = current;
-       if (tsk->flags & PF_LESS_THROTTLE || rt_task(tsk)) {
+       if (rt_task(tsk)) {
                bg_thresh += bg_thresh / 4 + global_wb_domain.dirty_limit / 32;
                thresh += thresh / 4 + global_wb_domain.dirty_limit / 32;
        }
@@ -486,7 +485,7 @@ static unsigned long node_dirty_limit(struct pglist_data *pgdat)
        else
                dirty = vm_dirty_ratio * node_memory / 100;
 
-       if (tsk->flags & PF_LESS_THROTTLE || rt_task(tsk))
+       if (rt_task(tsk))
                dirty += dirty / 4;
 
        return dirty;
@@ -505,7 +504,6 @@ bool node_dirty_ok(struct pglist_data *pgdat)
        unsigned long nr_pages = 0;
 
        nr_pages += node_page_state(pgdat, NR_FILE_DIRTY);
-       nr_pages += node_page_state(pgdat, NR_UNSTABLE_NFS);
        nr_pages += node_page_state(pgdat, NR_WRITEBACK);
 
        return nr_pages <= limit;
@@ -759,7 +757,7 @@ static void mdtc_calc_avail(struct dirty_throttle_control *mdtc,
  * bounded by the bdi->min_ratio and/or bdi->max_ratio parameters, if set.
  *
  * Return: @wb's dirty limit in pages. The term "dirty" in the context of
- * dirty balancing includes all PG_dirty, PG_writeback and NFS unstable pages.
+ * dirty balancing includes all PG_dirty and PG_writeback pages.
  */
 static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc)
 {
@@ -1567,7 +1565,7 @@ static void balance_dirty_pages(struct bdi_writeback *wb,
        struct dirty_throttle_control * const mdtc = mdtc_valid(&mdtc_stor) ?
                                                     &mdtc_stor : NULL;
        struct dirty_throttle_control *sdtc;
-       unsigned long nr_reclaimable;   /* = file_dirty + unstable_nfs */
+       unsigned long nr_reclaimable;   /* = file_dirty */
        long period;
        long pause;
        long max_pause;
@@ -1587,14 +1585,7 @@ static void balance_dirty_pages(struct bdi_writeback *wb,
                unsigned long m_thresh = 0;
                unsigned long m_bg_thresh = 0;
 
-               /*
-                * Unstable writes are a feature of certain networked
-                * filesystems (i.e. NFS) in which data may have been
-                * written to the server's write cache, but has not yet
-                * been flushed to permanent storage.
-                */
-               nr_reclaimable = global_node_page_state(NR_FILE_DIRTY) +
-                                       global_node_page_state(NR_UNSTABLE_NFS);
+               nr_reclaimable = global_node_page_state(NR_FILE_DIRTY);
                gdtc->avail = global_dirtyable_memory();
                gdtc->dirty = nr_reclaimable + global_node_page_state(NR_WRITEBACK);
 
@@ -1653,8 +1644,12 @@ static void balance_dirty_pages(struct bdi_writeback *wb,
                if (dirty <= dirty_freerun_ceiling(thresh, bg_thresh) &&
                    (!mdtc ||
                     m_dirty <= dirty_freerun_ceiling(m_thresh, m_bg_thresh))) {
-                       unsigned long intv = dirty_poll_interval(dirty, thresh);
-                       unsigned long m_intv = ULONG_MAX;
+                       unsigned long intv;
+                       unsigned long m_intv;
+
+free_running:
+                       intv = dirty_poll_interval(dirty, thresh);
+                       m_intv = ULONG_MAX;
 
                        current->dirty_paused_when = now;
                        current->nr_dirtied = 0;
@@ -1673,9 +1668,20 @@ static void balance_dirty_pages(struct bdi_writeback *wb,
                 * Calculate global domain's pos_ratio and select the
                 * global dtc by default.
                 */
-               if (!strictlimit)
+               if (!strictlimit) {
                        wb_dirty_limits(gdtc);
 
+                       if ((current->flags & PF_LOCAL_THROTTLE) &&
+                           gdtc->wb_dirty <
+                           dirty_freerun_ceiling(gdtc->wb_thresh,
+                                                 gdtc->wb_bg_thresh))
+                               /*
+                                * LOCAL_THROTTLE tasks must not be throttled
+                                * when below the per-wb freerun ceiling.
+                                */
+                               goto free_running;
+               }
+
                dirty_exceeded = (gdtc->wb_dirty > gdtc->wb_thresh) &&
                        ((gdtc->dirty > gdtc->thresh) || strictlimit);
 
@@ -1689,9 +1695,20 @@ static void balance_dirty_pages(struct bdi_writeback *wb,
                         * both global and memcg domains.  Choose the one
                         * w/ lower pos_ratio.
                         */
-                       if (!strictlimit)
+                       if (!strictlimit) {
                                wb_dirty_limits(mdtc);
 
+                               if ((current->flags & PF_LOCAL_THROTTLE) &&
+                                   mdtc->wb_dirty <
+                                   dirty_freerun_ceiling(mdtc->wb_thresh,
+                                                         mdtc->wb_bg_thresh))
+                                       /*
+                                        * LOCAL_THROTTLE tasks must not be
+                                        * throttled when below the per-wb
+                                        * freerun ceiling.
+                                        */
+                                       goto free_running;
+                       }
                        dirty_exceeded |= (mdtc->wb_dirty > mdtc->wb_thresh) &&
                                ((mdtc->dirty > mdtc->thresh) || strictlimit);
 
@@ -1938,8 +1955,7 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb)
         * as we're trying to decide whether to put more under writeback.
         */
        gdtc->avail = global_dirtyable_memory();
-       gdtc->dirty = global_node_page_state(NR_FILE_DIRTY) +
-                     global_node_page_state(NR_UNSTABLE_NFS);
+       gdtc->dirty = global_node_page_state(NR_FILE_DIRTY);
        domain_dirty_limits(gdtc);
 
        if (gdtc->dirty > gdtc->bg_thresh)
@@ -2164,7 +2180,6 @@ int write_cache_pages(struct address_space *mapping,
        int error;
        struct pagevec pvec;
        int nr_pages;
-       pgoff_t uninitialized_var(writeback_index);
        pgoff_t index;
        pgoff_t end;            /* Inclusive */
        pgoff_t done_index;
@@ -2173,8 +2188,7 @@ int write_cache_pages(struct address_space *mapping,
 
        pagevec_init(&pvec);
        if (wbc->range_cyclic) {
-               writeback_index = mapping->writeback_index; /* prev offset */
-               index = writeback_index;
+               index = mapping->writeback_index; /* prev offset */
                end = -1;
        } else {
                index = wbc->range_start >> PAGE_SHIFT;
index 13cc653122b73278afaeb6054539c619b164d11a..ca864102bebe969890a0a16732f2a1abb97864c6 100644 (file)
@@ -5319,7 +5319,7 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
 
        printk("active_anon:%lu inactive_anon:%lu isolated_anon:%lu\n"
                " active_file:%lu inactive_file:%lu isolated_file:%lu\n"
-               " unevictable:%lu dirty:%lu writeback:%lu unstable:%lu\n"
+               " unevictable:%lu dirty:%lu writeback:%lu\n"
                " slab_reclaimable:%lu slab_unreclaimable:%lu\n"
                " mapped:%lu shmem:%lu pagetables:%lu bounce:%lu\n"
                " free:%lu free_pcp:%lu free_cma:%lu\n",
@@ -5332,7 +5332,6 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                global_node_page_state(NR_UNEVICTABLE),
                global_node_page_state(NR_FILE_DIRTY),
                global_node_page_state(NR_WRITEBACK),
-               global_node_page_state(NR_UNSTABLE_NFS),
                global_node_page_state(NR_SLAB_RECLAIMABLE),
                global_node_page_state(NR_SLAB_UNRECLAIMABLE),
                global_node_page_state(NR_FILE_MAPPED),
@@ -5365,7 +5364,6 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                        " anon_thp: %lukB"
 #endif
                        " writeback_tmp:%lukB"
-                       " unstable:%lukB"
                        " all_unreclaimable? %s"
                        "\n",
                        pgdat->node_id,
@@ -5387,7 +5385,6 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                        K(node_page_state(pgdat, NR_ANON_THPS) * HPAGE_PMD_NR),
 #endif
                        K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
-                       K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
                        pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES ?
                                "yes" : "no");
        }
@@ -5420,6 +5417,9 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                        " managed:%lukB"
                        " mlocked:%lukB"
                        " kernel_stack:%lukB"
+#ifdef CONFIG_SHADOW_CALL_STACK
+                       " shadow_call_stack:%lukB"
+#endif
                        " pagetables:%lukB"
                        " bounce:%lukB"
                        " free_pcp:%lukB"
@@ -5442,6 +5442,9 @@ void show_free_areas(unsigned int filter, nodemask_t *nodemask)
                        K(zone_managed_pages(zone)),
                        K(zone_page_state(zone, NR_MLOCK)),
                        zone_page_state(zone, NR_KERNEL_STACK_KB),
+#ifdef CONFIG_SHADOW_CALL_STACK
+                       zone_page_state(zone, NR_KERNEL_SCS_KB),
+#endif
                        K(zone_page_state(zone, NR_PAGETABLE)),
                        K(zone_page_state(zone, NR_BOUNCE)),
                        K(free_pcp),
@@ -8247,7 +8250,7 @@ void *__init alloc_large_system_hash(const char *tablename,
                                table = memblock_alloc_raw(size,
                                                           SMP_CACHE_BYTES);
                } else if (get_order(size) >= MAX_ORDER || hashdist) {
-                       table = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+                       table = __vmalloc(size, gfp_flags);
                        virt = true;
                } else {
                        /*
index 7da7d7737dab3ddcdb14ee55e61b934c28bf8291..696367b1822215a7135d5b79f46e94b92548c807 100644 (file)
@@ -482,7 +482,7 @@ static void *pcpu_mem_zalloc(size_t size, gfp_t gfp)
        if (size <= PAGE_SIZE)
                return kzalloc(size, gfp);
        else
-               return __vmalloc(size, gfp | __GFP_ZERO, PAGE_KERNEL);
+               return __vmalloc(size, gfp | __GFP_ZERO);
 }
 
 /**
index 26208d0d03b7a9745981259801e445cf67bb7f1c..f4ce916f56020736b9f5804c11c28dd94aa6b7f7 100644 (file)
@@ -36,6 +36,9 @@ static int ptdump_pgd_entry(pgd_t *pgd, unsigned long addr,
                return note_kasan_page_table(walk, addr);
 #endif
 
+       if (st->effective_prot)
+               st->effective_prot(st, 0, pgd_val(val));
+
        if (pgd_leaf(val))
                st->note_page(st, addr, 0, pgd_val(val));
 
@@ -53,6 +56,9 @@ static int ptdump_p4d_entry(p4d_t *p4d, unsigned long addr,
                return note_kasan_page_table(walk, addr);
 #endif
 
+       if (st->effective_prot)
+               st->effective_prot(st, 1, p4d_val(val));
+
        if (p4d_leaf(val))
                st->note_page(st, addr, 1, p4d_val(val));
 
@@ -70,6 +76,9 @@ static int ptdump_pud_entry(pud_t *pud, unsigned long addr,
                return note_kasan_page_table(walk, addr);
 #endif
 
+       if (st->effective_prot)
+               st->effective_prot(st, 2, pud_val(val));
+
        if (pud_leaf(val))
                st->note_page(st, addr, 2, pud_val(val));
 
@@ -87,6 +96,8 @@ static int ptdump_pmd_entry(pmd_t *pmd, unsigned long addr,
                return note_kasan_page_table(walk, addr);
 #endif
 
+       if (st->effective_prot)
+               st->effective_prot(st, 3, pmd_val(val));
        if (pmd_leaf(val))
                st->note_page(st, addr, 3, pmd_val(val));
 
@@ -97,8 +108,12 @@ static int ptdump_pte_entry(pte_t *pte, unsigned long addr,
                            unsigned long next, struct mm_walk *walk)
 {
        struct ptdump_state *st = walk->private;
+       pte_t val = READ_ONCE(*pte);
+
+       if (st->effective_prot)
+               st->effective_prot(st, 4, pte_val(val));
 
-       st->note_page(st, addr, 4, pte_val(READ_ONCE(*pte)));
+       st->note_page(st, addr, 4, pte_val(val));
 
        return 0;
 }
index 2fe72cd29b472fab0090a6b75bc11bb4dc6d18c6..3c9a8dd7c56c86f9b868d8d9d7e745602a38ea75 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mm_inline.h>
 #include <linux/blk-cgroup.h>
 #include <linux/fadvise.h>
+#include <linux/sched/mm.h>
 
 #include "internal.h"
 
@@ -113,94 +114,126 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
 
 EXPORT_SYMBOL(read_cache_pages);
 
-static int read_pages(struct address_space *mapping, struct file *filp,
-               struct list_head *pages, unsigned int nr_pages, gfp_t gfp)
+static void read_pages(struct readahead_control *rac, struct list_head *pages,
+               bool skip_page)
 {
+       const struct address_space_operations *aops = rac->mapping->a_ops;
+       struct page *page;
        struct blk_plug plug;
-       unsigned page_idx;
-       int ret;
+
+       if (!readahead_count(rac))
+               goto out;
 
        blk_start_plug(&plug);
 
-       if (mapping->a_ops->readpages) {
-               ret = mapping->a_ops->readpages(filp, mapping, pages, nr_pages);
+       if (aops->readahead) {
+               aops->readahead(rac);
+               /* Clean up the remaining pages */
+               while ((page = readahead_page(rac))) {
+                       unlock_page(page);
+                       put_page(page);
+               }
+       } else if (aops->readpages) {
+               aops->readpages(rac->file, rac->mapping, pages,
+                               readahead_count(rac));
                /* Clean up the remaining pages */
                put_pages_list(pages);
-               goto out;
-       }
-
-       for (page_idx = 0; page_idx < nr_pages; page_idx++) {
-               struct page *page = lru_to_page(pages);
-               list_del(&page->lru);
-               if (!add_to_page_cache_lru(page, mapping, page->index, gfp))
-                       mapping->a_ops->readpage(filp, page);
-               put_page(page);
+               rac->_index += rac->_nr_pages;
+               rac->_nr_pages = 0;
+       } else {
+               while ((page = readahead_page(rac))) {
+                       aops->readpage(rac->file, page);
+                       put_page(page);
+               }
        }
-       ret = 0;
 
-out:
        blk_finish_plug(&plug);
 
-       return ret;
+       BUG_ON(!list_empty(pages));
+       BUG_ON(readahead_count(rac));
+
+out:
+       if (skip_page)
+               rac->_index++;
 }
 
-/*
- * __do_page_cache_readahead() actually reads a chunk of disk.  It allocates
- * the pages first, then submits them for I/O. This avoids the very bad
- * behaviour which would occur if page allocations are causing VM writeback.
- * We really don't want to intermingle reads and writes like that.
+/**
+ * page_cache_readahead_unbounded - Start unchecked readahead.
+ * @mapping: File address space.
+ * @file: This instance of the open file; used for authentication.
+ * @index: First page index to read.
+ * @nr_to_read: The number of pages to read.
+ * @lookahead_size: Where to start the next readahead.
  *
- * Returns the number of pages requested, or the maximum amount of I/O allowed.
+ * This function is for filesystems to call when they want to start
+ * readahead beyond a file's stated i_size.  This is almost certainly
+ * not the function you want to call.  Use page_cache_async_readahead()
+ * or page_cache_sync_readahead() instead.
+ *
+ * Context: File is referenced by caller.  Mutexes may be held by caller.
+ * May sleep, but will not reenter filesystem to reclaim memory.
  */
-unsigned int __do_page_cache_readahead(struct address_space *mapping,
-               struct file *filp, pgoff_t offset, unsigned long nr_to_read,
+void page_cache_readahead_unbounded(struct address_space *mapping,
+               struct file *file, pgoff_t index, unsigned long nr_to_read,
                unsigned long lookahead_size)
 {
-       struct inode *inode = mapping->host;
-       struct page *page;
-       unsigned long end_index;        /* The last page we want to read */
        LIST_HEAD(page_pool);
-       int page_idx;
-       unsigned int nr_pages = 0;
-       loff_t isize = i_size_read(inode);
        gfp_t gfp_mask = readahead_gfp_mask(mapping);
+       struct readahead_control rac = {
+               .mapping = mapping,
+               .file = file,
+               ._index = index,
+       };
+       unsigned long i;
 
-       if (isize == 0)
-               goto out;
-
-       end_index = ((isize - 1) >> PAGE_SHIFT);
+       /*
+        * Partway through the readahead operation, we will have added
+        * locked pages to the page cache, but will not yet have submitted
+        * them for I/O.  Adding another page may need to allocate memory,
+        * which can trigger memory reclaim.  Telling the VM we're in
+        * the middle of a filesystem operation will cause it to not
+        * touch file-backed pages, preventing a deadlock.  Most (all?)
+        * filesystems already specify __GFP_NOFS in their mapping's
+        * gfp_mask, but let's be explicit here.
+        */
+       unsigned int nofs = memalloc_nofs_save();
 
        /*
         * Preallocate as many pages as we will need.
         */
-       for (page_idx = 0; page_idx < nr_to_read; page_idx++) {
-               pgoff_t page_offset = offset + page_idx;
+       for (i = 0; i < nr_to_read; i++) {
+               struct page *page = xa_load(&mapping->i_pages, index + i);
 
-               if (page_offset > end_index)
-                       break;
+               BUG_ON(index + i != rac._index + rac._nr_pages);
 
-               page = xa_load(&mapping->i_pages, page_offset);
                if (page && !xa_is_value(page)) {
                        /*
-                        * Page already present?  Kick off the current batch of
-                        * contiguous pages before continuing with the next
-                        * batch.
+                        * Page already present?  Kick off the current batch
+                        * of contiguous pages before continuing with the
+                        * next batch.  This page may be the one we would
+                        * have intended to mark as Readahead, but we don't
+                        * have a stable reference to this page, and it's
+                        * not worth getting one just for that.
                         */
-                       if (nr_pages)
-                               read_pages(mapping, filp, &page_pool, nr_pages,
-                                               gfp_mask);
-                       nr_pages = 0;
+                       read_pages(&rac, &page_pool, true);
                        continue;
                }
 
                page = __page_cache_alloc(gfp_mask);
                if (!page)
                        break;
-               page->index = page_offset;
-               list_add(&page->lru, &page_pool);
-               if (page_idx == nr_to_read - lookahead_size)
+               if (mapping->a_ops->readpages) {
+                       page->index = index + i;
+                       list_add(&page->lru, &page_pool);
+               } else if (add_to_page_cache_lru(page, mapping, index + i,
+                                       gfp_mask) < 0) {
+                       put_page(page);
+                       read_pages(&rac, &page_pool, true);
+                       continue;
+               }
+               if (i == nr_to_read - lookahead_size)
                        SetPageReadahead(page);
-               nr_pages++;
+               rac._nr_pages++;
        }
 
        /*
@@ -208,26 +241,53 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping,
         * uptodate then the caller will launch readpage again, and
         * will then handle the error.
         */
-       if (nr_pages)
-               read_pages(mapping, filp, &page_pool, nr_pages, gfp_mask);
-       BUG_ON(!list_empty(&page_pool));
-out:
-       return nr_pages;
+       read_pages(&rac, &page_pool, false);
+       memalloc_nofs_restore(nofs);
+}
+EXPORT_SYMBOL_GPL(page_cache_readahead_unbounded);
+
+/*
+ * __do_page_cache_readahead() actually reads a chunk of disk.  It allocates
+ * the pages first, then submits them for I/O. This avoids the very bad
+ * behaviour which would occur if page allocations are causing VM writeback.
+ * We really don't want to intermingle reads and writes like that.
+ */
+void __do_page_cache_readahead(struct address_space *mapping,
+               struct file *file, pgoff_t index, unsigned long nr_to_read,
+               unsigned long lookahead_size)
+{
+       struct inode *inode = mapping->host;
+       loff_t isize = i_size_read(inode);
+       pgoff_t end_index;      /* The last page we want to read */
+
+       if (isize == 0)
+               return;
+
+       end_index = (isize - 1) >> PAGE_SHIFT;
+       if (index > end_index)
+               return;
+       /* Don't read past the page containing the last byte of the file */
+       if (nr_to_read > end_index - index)
+               nr_to_read = end_index - index + 1;
+
+       page_cache_readahead_unbounded(mapping, file, index, nr_to_read,
+                       lookahead_size);
 }
 
 /*
  * Chunk the readahead into 2 megabyte units, so that we don't pin too much
  * memory at once.
  */
-int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
-                              pgoff_t offset, unsigned long nr_to_read)
+void force_page_cache_readahead(struct address_space *mapping,
+               struct file *filp, pgoff_t index, unsigned long nr_to_read)
 {
        struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
        struct file_ra_state *ra = &filp->f_ra;
        unsigned long max_pages;
 
-       if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages))
-               return -EINVAL;
+       if (unlikely(!mapping->a_ops->readpage && !mapping->a_ops->readpages &&
+                       !mapping->a_ops->readahead))
+               return;
 
        /*
         * If the request exceeds the readahead window, allow the read to
@@ -240,12 +300,11 @@ int force_page_cache_readahead(struct address_space *mapping, struct file *filp,
 
                if (this_chunk > nr_to_read)
                        this_chunk = nr_to_read;
-               __do_page_cache_readahead(mapping, filp, offset, this_chunk, 0);
+               __do_page_cache_readahead(mapping, filp, index, this_chunk, 0);
 
-               offset += this_chunk;
+               index += this_chunk;
                nr_to_read -= this_chunk;
        }
-       return 0;
 }
 
 /*
@@ -324,21 +383,21 @@ static unsigned long get_next_ra_size(struct file_ra_state *ra,
  */
 
 /*
- * Count contiguously cached pages from @offset-1 to @offset-@max,
+ * Count contiguously cached pages from @index-1 to @index-@max,
  * this count is a conservative estimation of
  *     - length of the sequential read sequence, or
  *     - thrashing threshold in memory tight systems
  */
 static pgoff_t count_history_pages(struct address_space *mapping,
-                                  pgoff_t offset, unsigned long max)
+                                  pgoff_t index, unsigned long max)
 {
        pgoff_t head;
 
        rcu_read_lock();
-       head = page_cache_prev_miss(mapping, offset - 1, max);
+       head = page_cache_prev_miss(mapping, index - 1, max);
        rcu_read_unlock();
 
-       return offset - 1 - head;
+       return index - 1 - head;
 }
 
 /*
@@ -346,13 +405,13 @@ static pgoff_t count_history_pages(struct address_space *mapping,
  */
 static int try_context_readahead(struct address_space *mapping,
                                 struct file_ra_state *ra,
-                                pgoff_t offset,
+                                pgoff_t index,
                                 unsigned long req_size,
                                 unsigned long max)
 {
        pgoff_t size;
 
-       size = count_history_pages(mapping, offset, max);
+       size = count_history_pages(mapping, index, max);
 
        /*
         * not enough history pages:
@@ -365,10 +424,10 @@ static int try_context_readahead(struct address_space *mapping,
         * starts from beginning of file:
         * it is a strong indication of long-run stream (or whole-file-read)
         */
-       if (size >= offset)
+       if (size >= index)
                size *= 2;
 
-       ra->start = offset;
+       ra->start = index;
        ra->size = min(size + req_size, max);
        ra->async_size = 1;
 
@@ -378,16 +437,15 @@ static int try_context_readahead(struct address_space *mapping,
 /*
  * A minimal readahead algorithm for trivial sequential/random reads.
  */
-static unsigned long
-ondemand_readahead(struct address_space *mapping,
-                  struct file_ra_state *ra, struct file *filp,
-                  bool hit_readahead_marker, pgoff_t offset,
-                  unsigned long req_size)
+static void ondemand_readahead(struct address_space *mapping,
+               struct file_ra_state *ra, struct file *filp,
+               bool hit_readahead_marker, pgoff_t index,
+               unsigned long req_size)
 {
        struct backing_dev_info *bdi = inode_to_bdi(mapping->host);
        unsigned long max_pages = ra->ra_pages;
        unsigned long add_pages;
-       pgoff_t prev_offset;
+       pgoff_t prev_index;
 
        /*
         * If the request exceeds the readahead window, allow the read to
@@ -399,15 +457,15 @@ ondemand_readahead(struct address_space *mapping,
        /*
         * start of file
         */
-       if (!offset)
+       if (!index)
                goto initial_readahead;
 
        /*
-        * It's the expected callback offset, assume sequential access.
+        * It's the expected callback index, assume sequential access.
         * Ramp up sizes, and push forward the readahead window.
         */
-       if ((offset == (ra->start + ra->size - ra->async_size) ||
-            offset == (ra->start + ra->size))) {
+       if ((index == (ra->start + ra->size - ra->async_size) ||
+            index == (ra->start + ra->size))) {
                ra->start += ra->size;
                ra->size = get_next_ra_size(ra, max_pages);
                ra->async_size = ra->size;
@@ -424,14 +482,14 @@ ondemand_readahead(struct address_space *mapping,
                pgoff_t start;
 
                rcu_read_lock();
-               start = page_cache_next_miss(mapping, offset + 1, max_pages);
+               start = page_cache_next_miss(mapping, index + 1, max_pages);
                rcu_read_unlock();
 
-               if (!start || start - offset > max_pages)
-                       return 0;
+               if (!start || start - index > max_pages)
+                       return;
 
                ra->start = start;
-               ra->size = start - offset;      /* old async_size */
+               ra->size = start - index;       /* old async_size */
                ra->size += req_size;
                ra->size = get_next_ra_size(ra, max_pages);
                ra->async_size = ra->size;
@@ -446,28 +504,29 @@ ondemand_readahead(struct address_space *mapping,
 
        /*
         * sequential cache miss
-        * trivial case: (offset - prev_offset) == 1
-        * unaligned reads: (offset - prev_offset) == 0
+        * trivial case: (index - prev_index) == 1
+        * unaligned reads: (index - prev_index) == 0
         */
-       prev_offset = (unsigned long long)ra->prev_pos >> PAGE_SHIFT;
-       if (offset - prev_offset <= 1UL)
+       prev_index = (unsigned long long)ra->prev_pos >> PAGE_SHIFT;
+       if (index - prev_index <= 1UL)
                goto initial_readahead;
 
        /*
         * Query the page cache and look for the traces(cached history pages)
         * that a sequential stream would leave behind.
         */
-       if (try_context_readahead(mapping, ra, offset, req_size, max_pages))
+       if (try_context_readahead(mapping, ra, index, req_size, max_pages))
                goto readit;
 
        /*
         * standalone, small random read
         * Read as is, and do not pollute the readahead state.
         */
-       return __do_page_cache_readahead(mapping, filp, offset, req_size, 0);
+       __do_page_cache_readahead(mapping, filp, index, req_size, 0);
+       return;
 
 initial_readahead:
-       ra->start = offset;
+       ra->start = index;
        ra->size = get_init_ra_size(req_size, max_pages);
        ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;
 
@@ -478,7 +537,7 @@ readit:
         * the resulted next readahead window into the current one.
         * Take care of maximum IO pages as above.
         */
-       if (offset == ra->start && ra->size == ra->async_size) {
+       if (index == ra->start && ra->size == ra->async_size) {
                add_pages = get_next_ra_size(ra, max_pages);
                if (ra->size + add_pages <= max_pages) {
                        ra->async_size = add_pages;
@@ -489,7 +548,7 @@ readit:
                }
        }
 
-       return ra_submit(ra, mapping, filp);
+       ra_submit(ra, mapping, filp);
 }
 
 /**
@@ -497,9 +556,8 @@ readit:
  * @mapping: address_space which holds the pagecache and I/O vectors
  * @ra: file_ra_state which holds the readahead state
  * @filp: passed on to ->readpage() and ->readpages()
- * @offset: start offset into @mapping, in pagecache page-sized units
- * @req_size: hint: total size of the read which the caller is performing in
- *            pagecache pages
+ * @index: Index of first page to be read.
+ * @req_count: Total number of pages being read by the caller.
  *
  * page_cache_sync_readahead() should be called when a cache miss happened:
  * it will submit the read.  The readahead logic may decide to piggyback more
@@ -508,7 +566,7 @@ readit:
  */
 void page_cache_sync_readahead(struct address_space *mapping,
                               struct file_ra_state *ra, struct file *filp,
-                              pgoff_t offset, unsigned long req_size)
+                              pgoff_t index, unsigned long req_count)
 {
        /* no read-ahead */
        if (!ra->ra_pages)
@@ -519,12 +577,12 @@ void page_cache_sync_readahead(struct address_space *mapping,
 
        /* be dumb */
        if (filp && (filp->f_mode & FMODE_RANDOM)) {
-               force_page_cache_readahead(mapping, filp, offset, req_size);
+               force_page_cache_readahead(mapping, filp, index, req_count);
                return;
        }
 
        /* do read-ahead */
-       ondemand_readahead(mapping, ra, filp, false, offset, req_size);
+       ondemand_readahead(mapping, ra, filp, false, index, req_count);
 }
 EXPORT_SYMBOL_GPL(page_cache_sync_readahead);
 
@@ -533,21 +591,20 @@ EXPORT_SYMBOL_GPL(page_cache_sync_readahead);
  * @mapping: address_space which holds the pagecache and I/O vectors
  * @ra: file_ra_state which holds the readahead state
  * @filp: passed on to ->readpage() and ->readpages()
- * @page: the page at @offset which has the PG_readahead flag set
- * @offset: start offset into @mapping, in pagecache page-sized units
- * @req_size: hint: total size of the read which the caller is performing in
- *            pagecache pages
+ * @page: The page at @index which triggered the readahead call.
+ * @index: Index of first page to be read.
+ * @req_count: Total number of pages being read by the caller.
  *
  * page_cache_async_readahead() should be called when a page is used which
- * has the PG_readahead flag; this is a marker to suggest that the application
+ * is marked as PageReadahead; this is a marker to suggest that the application
  * has used up enough of the readahead window that we should start pulling in
  * more pages.
  */
 void
 page_cache_async_readahead(struct address_space *mapping,
                           struct file_ra_state *ra, struct file *filp,
-                          struct page *page, pgoff_t offset,
-                          unsigned long req_size)
+                          struct page *page, pgoff_t index,
+                          unsigned long req_count)
 {
        /* no read-ahead */
        if (!ra->ra_pages)
@@ -571,7 +628,7 @@ page_cache_async_readahead(struct address_space *mapping,
                return;
 
        /* do read-ahead */
-       ondemand_readahead(mapping, ra, filp, true, offset, req_size);
+       ondemand_readahead(mapping, ra, filp, true, index, req_count);
 }
 EXPORT_SYMBOL_GPL(page_cache_async_readahead);
 
index 23c7500eea7d9197fa41b5d3ace65098aa7664b0..9e72ba2241750c4a8b8c32d84c9f9a1a79e4fb0e 100644 (file)
@@ -1303,7 +1303,8 @@ void __init create_kmalloc_caches(slab_flags_t flags)
                        kmalloc_caches[KMALLOC_DMA][i] = create_kmalloc_cache(
                                kmalloc_info[i].name[KMALLOC_DMA],
                                kmalloc_info[i].size,
-                               SLAB_CACHE_DMA | flags, 0, 0);
+                               SLAB_CACHE_DMA | flags, 0,
+                               kmalloc_info[i].size);
                }
        }
 #endif
index b762450fc9f076841a410e8b51067d54beb569a6..2c56cc9e4ff2407bd94100c43c0b14c262dba54a 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -679,6 +679,20 @@ static void slab_fix(struct kmem_cache *s, char *fmt, ...)
        va_end(args);
 }
 
+static bool freelist_corrupted(struct kmem_cache *s, struct page *page,
+                              void *freelist, void *nextfree)
+{
+       if ((s->flags & SLAB_CONSISTENCY_CHECKS) &&
+           !check_valid_pointer(s, page, nextfree)) {
+               object_err(s, page, freelist, "Freechain corrupt");
+               freelist = NULL;
+               slab_fix(s, "Isolate corrupted freechain");
+               return true;
+       }
+
+       return false;
+}
+
 static void print_trailer(struct kmem_cache *s, struct page *page, u8 *p)
 {
        unsigned int off;       /* Offset of last byte */
@@ -1410,6 +1424,11 @@ static inline void inc_slabs_node(struct kmem_cache *s, int node,
 static inline void dec_slabs_node(struct kmem_cache *s, int node,
                                                        int objects) {}
 
+static bool freelist_corrupted(struct kmem_cache *s, struct page *page,
+                              void *freelist, void *nextfree)
+{
+       return false;
+}
 #endif /* CONFIG_SLUB_DEBUG */
 
 /*
@@ -2093,6 +2112,14 @@ static void deactivate_slab(struct kmem_cache *s, struct page *page,
                void *prior;
                unsigned long counters;
 
+               /*
+                * If 'nextfree' is invalid, it is possible that the object at
+                * 'freelist' is already corrupted.  So isolate all objects
+                * starting at 'freelist'.
+                */
+               if (freelist_corrupted(s, page, freelist, nextfree))
+                       break;
+
                do {
                        prior = page->freelist;
                        counters = page->counters;
@@ -3739,12 +3766,14 @@ error:
 }
 
 static void list_slab_objects(struct kmem_cache *s, struct page *page,
-                                                       const char *text)
+                             const char *text, unsigned long *map)
 {
 #ifdef CONFIG_SLUB_DEBUG
        void *addr = page_address(page);
        void *p;
-       unsigned long *map;
+
+       if (!map)
+               return;
 
        slab_err(s, page, text, s->name);
        slab_lock(page);
@@ -3757,8 +3786,6 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page,
                        print_tracking(s, p);
                }
        }
-       put_map(map);
-
        slab_unlock(page);
 #endif
 }
@@ -3772,6 +3799,11 @@ static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
 {
        LIST_HEAD(discard);
        struct page *page, *h;
+       unsigned long *map = NULL;
+
+#ifdef CONFIG_SLUB_DEBUG
+       map = bitmap_alloc(oo_objects(s->max), GFP_KERNEL);
+#endif
 
        BUG_ON(irqs_disabled());
        spin_lock_irq(&n->list_lock);
@@ -3781,11 +3813,16 @@ static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
                        list_add(&page->slab_list, &discard);
                } else {
                        list_slab_objects(s, page,
-                       "Objects remaining in %s on __kmem_cache_shutdown()");
+                         "Objects remaining in %s on __kmem_cache_shutdown()",
+                         map);
                }
        }
        spin_unlock_irq(&n->list_lock);
 
+#ifdef CONFIG_SLUB_DEBUG
+       bitmap_free(map);
+#endif
+
        list_for_each_entry_safe(page, h, &discard, slab_list)
                discard_slab(s, page);
 }
@@ -5654,7 +5691,8 @@ static void memcg_propagate_slab_attrs(struct kmem_cache *s)
                 */
                if (buffer)
                        buf = buffer;
-               else if (root_cache->max_attr_size < ARRAY_SIZE(mbuf))
+               else if (root_cache->max_attr_size < ARRAY_SIZE(mbuf) &&
+                        !IS_ENABLED(CONFIG_SLUB_STATS))
                        buf = mbuf;
                else {
                        buffer = (char *) get_zeroed_page(GFP_KERNEL);
@@ -5688,19 +5726,6 @@ static struct kobj_type slab_ktype = {
        .release = kmem_cache_release,
 };
 
-static int uevent_filter(struct kset *kset, struct kobject *kobj)
-{
-       struct kobj_type *ktype = get_ktype(kobj);
-
-       if (ktype == &slab_ktype)
-               return 1;
-       return 0;
-}
-
-static const struct kset_uevent_ops slab_uevent_ops = {
-       .filter = uevent_filter,
-};
-
 static struct kset *slab_kset;
 
 static inline struct kset *cache_kset(struct kmem_cache *s)
@@ -5768,7 +5793,6 @@ static void sysfs_slab_remove_workfn(struct work_struct *work)
 #ifdef CONFIG_MEMCG
        kset_unregister(s->memcg_kset);
 #endif
-       kobject_uevent(&s->kobj, KOBJ_REMOVE);
 out:
        kobject_put(&s->kobj);
 }
@@ -5826,7 +5850,6 @@ static int sysfs_slab_add(struct kmem_cache *s)
        }
 #endif
 
-       kobject_uevent(&s->kobj, KOBJ_ADD);
        if (!unmergeable) {
                /* Setup first alias */
                sysfs_slab_alias(s, s->name);
@@ -5907,7 +5930,7 @@ static int __init slab_sysfs_init(void)
 
        mutex_lock(&slab_mutex);
 
-       slab_kset = kset_create_and_add("slab", &slab_uevent_ops, kernel_kobj);
+       slab_kset = kset_create_and_add("slab", NULL, kernel_kobj);
        if (!slab_kset) {
                mutex_unlock(&slab_mutex);
                pr_err("Cannot register slab subsystem.\n");
index bf9a79fed62d7aed216ed2dd38f6e3557bd49172..0ac463d44cff4bc36dce077015db91620e4f337a 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -35,6 +35,7 @@
 #include <linux/uio.h>
 #include <linux/hugetlb.h>
 #include <linux/page_idle.h>
+#include <linux/local_lock.h>
 
 #include "internal.h"
 
 /* How many pages do we try to swap or page in/out together? */
 int page_cluster;
 
-static DEFINE_PER_CPU(struct pagevec, lru_add_pvec);
-static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs);
-static DEFINE_PER_CPU(struct pagevec, lru_deactivate_file_pvecs);
-static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs);
-static DEFINE_PER_CPU(struct pagevec, lru_lazyfree_pvecs);
+/* Protecting only lru_rotate.pvec which requires disabling interrupts */
+struct lru_rotate {
+       local_lock_t lock;
+       struct pagevec pvec;
+};
+static DEFINE_PER_CPU(struct lru_rotate, lru_rotate) = {
+       .lock = INIT_LOCAL_LOCK(lock),
+};
+
+/*
+ * The following struct pagevec are grouped together because they are protected
+ * by disabling preemption (and interrupts remain enabled).
+ */
+struct lru_pvecs {
+       local_lock_t lock;
+       struct pagevec lru_add;
+       struct pagevec lru_deactivate_file;
+       struct pagevec lru_deactivate;
+       struct pagevec lru_lazyfree;
 #ifdef CONFIG_SMP
-static DEFINE_PER_CPU(struct pagevec, activate_page_pvecs);
+       struct pagevec activate_page;
 #endif
+};
+static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = {
+       .lock = INIT_LOCAL_LOCK(lock),
+};
 
 /*
  * This path almost never happens for VM activity - pages are normally
@@ -254,11 +273,11 @@ void rotate_reclaimable_page(struct page *page)
                unsigned long flags;
 
                get_page(page);
-               local_irq_save(flags);
-               pvec = this_cpu_ptr(&lru_rotate_pvecs);
+               local_lock_irqsave(&lru_rotate.lock, flags);
+               pvec = this_cpu_ptr(&lru_rotate.pvec);
                if (!pagevec_add(pvec, page) || PageCompound(page))
                        pagevec_move_tail(pvec);
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&lru_rotate.lock, flags);
        }
 }
 
@@ -293,7 +312,7 @@ static void __activate_page(struct page *page, struct lruvec *lruvec,
 #ifdef CONFIG_SMP
 static void activate_page_drain(int cpu)
 {
-       struct pagevec *pvec = &per_cpu(activate_page_pvecs, cpu);
+       struct pagevec *pvec = &per_cpu(lru_pvecs.activate_page, cpu);
 
        if (pagevec_count(pvec))
                pagevec_lru_move_fn(pvec, __activate_page, NULL);
@@ -301,19 +320,21 @@ static void activate_page_drain(int cpu)
 
 static bool need_activate_page_drain(int cpu)
 {
-       return pagevec_count(&per_cpu(activate_page_pvecs, cpu)) != 0;
+       return pagevec_count(&per_cpu(lru_pvecs.activate_page, cpu)) != 0;
 }
 
 void activate_page(struct page *page)
 {
        page = compound_head(page);
        if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) {
-               struct pagevec *pvec = &get_cpu_var(activate_page_pvecs);
+               struct pagevec *pvec;
 
+               local_lock(&lru_pvecs.lock);
+               pvec = this_cpu_ptr(&lru_pvecs.activate_page);
                get_page(page);
                if (!pagevec_add(pvec, page) || PageCompound(page))
                        pagevec_lru_move_fn(pvec, __activate_page, NULL);
-               put_cpu_var(activate_page_pvecs);
+               local_unlock(&lru_pvecs.lock);
        }
 }
 
@@ -335,9 +356,12 @@ void activate_page(struct page *page)
 
 static void __lru_cache_activate_page(struct page *page)
 {
-       struct pagevec *pvec = &get_cpu_var(lru_add_pvec);
+       struct pagevec *pvec;
        int i;
 
+       local_lock(&lru_pvecs.lock);
+       pvec = this_cpu_ptr(&lru_pvecs.lru_add);
+
        /*
         * Search backwards on the optimistic assumption that the page being
         * activated has just been added to this pagevec. Note that only
@@ -357,7 +381,7 @@ static void __lru_cache_activate_page(struct page *page)
                }
        }
 
-       put_cpu_var(lru_add_pvec);
+       local_unlock(&lru_pvecs.lock);
 }
 
 /*
@@ -385,7 +409,7 @@ void mark_page_accessed(struct page *page)
        } else if (!PageActive(page)) {
                /*
                 * If the page is on the LRU, queue it for activation via
-                * activate_page_pvecs. Otherwise, assume the page is on a
+                * lru_pvecs.activate_page. Otherwise, assume the page is on a
                 * pagevec, mark it active and it'll be moved to the active
                 * LRU on the next drain.
                 */
@@ -404,12 +428,14 @@ EXPORT_SYMBOL(mark_page_accessed);
 
 static void __lru_cache_add(struct page *page)
 {
-       struct pagevec *pvec = &get_cpu_var(lru_add_pvec);
+       struct pagevec *pvec;
 
+       local_lock(&lru_pvecs.lock);
+       pvec = this_cpu_ptr(&lru_pvecs.lru_add);
        get_page(page);
        if (!pagevec_add(pvec, page) || PageCompound(page))
                __pagevec_lru_add(pvec);
-       put_cpu_var(lru_add_pvec);
+       local_unlock(&lru_pvecs.lock);
 }
 
 /**
@@ -593,30 +619,30 @@ static void lru_lazyfree_fn(struct page *page, struct lruvec *lruvec,
  */
 void lru_add_drain_cpu(int cpu)
 {
-       struct pagevec *pvec = &per_cpu(lru_add_pvec, cpu);
+       struct pagevec *pvec = &per_cpu(lru_pvecs.lru_add, cpu);
 
        if (pagevec_count(pvec))
                __pagevec_lru_add(pvec);
 
-       pvec = &per_cpu(lru_rotate_pvecs, cpu);
+       pvec = &per_cpu(lru_rotate.pvec, cpu);
        if (pagevec_count(pvec)) {
                unsigned long flags;
 
                /* No harm done if a racing interrupt already did this */
-               local_irq_save(flags);
+               local_lock_irqsave(&lru_rotate.lock, flags);
                pagevec_move_tail(pvec);
-               local_irq_restore(flags);
+               local_unlock_irqrestore(&lru_rotate.lock, flags);
        }
 
-       pvec = &per_cpu(lru_deactivate_file_pvecs, cpu);
+       pvec = &per_cpu(lru_pvecs.lru_deactivate_file, cpu);
        if (pagevec_count(pvec))
                pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL);
 
-       pvec = &per_cpu(lru_deactivate_pvecs, cpu);
+       pvec = &per_cpu(lru_pvecs.lru_deactivate, cpu);
        if (pagevec_count(pvec))
                pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL);
 
-       pvec = &per_cpu(lru_lazyfree_pvecs, cpu);
+       pvec = &per_cpu(lru_pvecs.lru_lazyfree, cpu);
        if (pagevec_count(pvec))
                pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL);
 
@@ -641,11 +667,14 @@ void deactivate_file_page(struct page *page)
                return;
 
        if (likely(get_page_unless_zero(page))) {
-               struct pagevec *pvec = &get_cpu_var(lru_deactivate_file_pvecs);
+               struct pagevec *pvec;
+
+               local_lock(&lru_pvecs.lock);
+               pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate_file);
 
                if (!pagevec_add(pvec, page) || PageCompound(page))
                        pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL);
-               put_cpu_var(lru_deactivate_file_pvecs);
+               local_unlock(&lru_pvecs.lock);
        }
 }
 
@@ -660,12 +689,14 @@ void deactivate_file_page(struct page *page)
 void deactivate_page(struct page *page)
 {
        if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) {
-               struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs);
+               struct pagevec *pvec;
 
+               local_lock(&lru_pvecs.lock);
+               pvec = this_cpu_ptr(&lru_pvecs.lru_deactivate);
                get_page(page);
                if (!pagevec_add(pvec, page) || PageCompound(page))
                        pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL);
-               put_cpu_var(lru_deactivate_pvecs);
+               local_unlock(&lru_pvecs.lock);
        }
 }
 
@@ -680,19 +711,30 @@ void mark_page_lazyfree(struct page *page)
 {
        if (PageLRU(page) && PageAnon(page) && PageSwapBacked(page) &&
            !PageSwapCache(page) && !PageUnevictable(page)) {
-               struct pagevec *pvec = &get_cpu_var(lru_lazyfree_pvecs);
+               struct pagevec *pvec;
 
+               local_lock(&lru_pvecs.lock);
+               pvec = this_cpu_ptr(&lru_pvecs.lru_lazyfree);
                get_page(page);
                if (!pagevec_add(pvec, page) || PageCompound(page))
                        pagevec_lru_move_fn(pvec, lru_lazyfree_fn, NULL);
-               put_cpu_var(lru_lazyfree_pvecs);
+               local_unlock(&lru_pvecs.lock);
        }
 }
 
 void lru_add_drain(void)
 {
-       lru_add_drain_cpu(get_cpu());
-       put_cpu();
+       local_lock(&lru_pvecs.lock);
+       lru_add_drain_cpu(smp_processor_id());
+       local_unlock(&lru_pvecs.lock);
+}
+
+void lru_add_drain_cpu_zone(struct zone *zone)
+{
+       local_lock(&lru_pvecs.lock);
+       lru_add_drain_cpu(smp_processor_id());
+       drain_local_pages(zone);
+       local_unlock(&lru_pvecs.lock);
 }
 
 #ifdef CONFIG_SMP
@@ -743,11 +785,11 @@ void lru_add_drain_all(void)
        for_each_online_cpu(cpu) {
                struct work_struct *work = &per_cpu(lru_add_drain_work, cpu);
 
-               if (pagevec_count(&per_cpu(lru_add_pvec, cpu)) ||
-                   pagevec_count(&per_cpu(lru_rotate_pvecs, cpu)) ||
-                   pagevec_count(&per_cpu(lru_deactivate_file_pvecs, cpu)) ||
-                   pagevec_count(&per_cpu(lru_deactivate_pvecs, cpu)) ||
-                   pagevec_count(&per_cpu(lru_lazyfree_pvecs, cpu)) ||
+               if (pagevec_count(&per_cpu(lru_pvecs.lru_add, cpu)) ||
+                   pagevec_count(&per_cpu(lru_rotate.pvec, cpu)) ||
+                   pagevec_count(&per_cpu(lru_pvecs.lru_deactivate_file, cpu)) ||
+                   pagevec_count(&per_cpu(lru_pvecs.lru_deactivate, cpu)) ||
+                   pagevec_count(&per_cpu(lru_pvecs.lru_lazyfree, cpu)) ||
                    need_activate_page_drain(cpu)) {
                        INIT_WORK(work, lru_add_drain_per_cpu);
                        queue_work_on(cpu, mm_percpu_wq, work);
index ebed37bbf7a39035ec31e92f63d927e9d65e2026..8238954ae781de048c414f0bb6825f7b349b8a9d 100644 (file)
@@ -509,10 +509,11 @@ static unsigned long swapin_nr_pages(unsigned long offset)
                return 1;
 
        hits = atomic_xchg(&swapin_readahead_hits, 0);
-       pages = __swapin_nr_pages(prev_offset, offset, hits, max_pages,
+       pages = __swapin_nr_pages(READ_ONCE(prev_offset), offset, hits,
+                                 max_pages,
                                  atomic_read(&last_readahead_pages));
        if (!hits)
-               prev_offset = offset;
+               WRITE_ONCE(prev_offset, offset);
        atomic_set(&last_readahead_pages, pages);
 
        return pages;
index 5871a2aa86a57d4a43d2edb8d76fa083265cfde0..63ac67208453c0953575035cc68f2c566be974b9 100644 (file)
@@ -601,7 +601,6 @@ static bool scan_swap_map_try_ssd_cluster(struct swap_info_struct *si,
 {
        struct percpu_cluster *cluster;
        struct swap_cluster_info *ci;
-       bool found_free;
        unsigned long tmp, max;
 
 new_cluster:
@@ -614,17 +613,17 @@ new_cluster:
                } else if (!cluster_list_empty(&si->discard_clusters)) {
                        /*
                         * we don't have free cluster but have some clusters in
-                        * discarding, do discard now and reclaim them
+                        * discarding, do discard now and reclaim them, then
+                        * reread cluster_next_cpu since we dropped si->lock
                         */
                        swap_do_scheduled_discard(si);
-                       *scan_base = *offset = si->cluster_next;
+                       *scan_base = this_cpu_read(*si->cluster_next_cpu);
+                       *offset = *scan_base;
                        goto new_cluster;
                } else
                        return false;
        }
 
-       found_free = false;
-
        /*
         * Other CPUs can use our cluster if they can't find a free cluster,
         * check if there is still free entry in the cluster
@@ -632,27 +631,23 @@ new_cluster:
        tmp = cluster->next;
        max = min_t(unsigned long, si->max,
                    (cluster_next(&cluster->index) + 1) * SWAPFILE_CLUSTER);
-       if (tmp >= max) {
-               cluster_set_null(&cluster->index);
-               goto new_cluster;
-       }
-       ci = lock_cluster(si, tmp);
-       while (tmp < max) {
-               if (!si->swap_map[tmp]) {
-                       found_free = true;
-                       break;
+       if (tmp < max) {
+               ci = lock_cluster(si, tmp);
+               while (tmp < max) {
+                       if (!si->swap_map[tmp])
+                               break;
+                       tmp++;
                }
-               tmp++;
+               unlock_cluster(ci);
        }
-       unlock_cluster(ci);
-       if (!found_free) {
+       if (tmp >= max) {
                cluster_set_null(&cluster->index);
                goto new_cluster;
        }
        cluster->next = tmp + 1;
        *offset = tmp;
        *scan_base = tmp;
-       return found_free;
+       return true;
 }
 
 static void __del_from_avail_list(struct swap_info_struct *p)
@@ -729,6 +724,34 @@ static void swap_range_free(struct swap_info_struct *si, unsigned long offset,
        }
 }
 
+static void set_cluster_next(struct swap_info_struct *si, unsigned long next)
+{
+       unsigned long prev;
+
+       if (!(si->flags & SWP_SOLIDSTATE)) {
+               si->cluster_next = next;
+               return;
+       }
+
+       prev = this_cpu_read(*si->cluster_next_cpu);
+       /*
+        * Cross the swap address space size aligned trunk, choose
+        * another trunk randomly to avoid lock contention on swap
+        * address space if possible.
+        */
+       if ((prev >> SWAP_ADDRESS_SPACE_SHIFT) !=
+           (next >> SWAP_ADDRESS_SPACE_SHIFT)) {
+               /* No free swap slots available */
+               if (si->highest_bit <= si->lowest_bit)
+                       return;
+               next = si->lowest_bit +
+                       prandom_u32_max(si->highest_bit - si->lowest_bit + 1);
+               next = ALIGN_DOWN(next, SWAP_ADDRESS_SPACE_PAGES);
+               next = max_t(unsigned int, next, si->lowest_bit);
+       }
+       this_cpu_write(*si->cluster_next_cpu, next);
+}
+
 static int scan_swap_map_slots(struct swap_info_struct *si,
                               unsigned char usage, int nr,
                               swp_entry_t slots[])
@@ -739,9 +762,7 @@ static int scan_swap_map_slots(struct swap_info_struct *si,
        unsigned long last_in_cluster = 0;
        int latency_ration = LATENCY_LIMIT;
        int n_ret = 0;
-
-       if (nr > SWAP_BATCH)
-               nr = SWAP_BATCH;
+       bool scanned_many = false;
 
        /*
         * We try to cluster swap pages by allocating them sequentially
@@ -755,17 +776,22 @@ static int scan_swap_map_slots(struct swap_info_struct *si,
         */
 
        si->flags += SWP_SCANNING;
-       scan_base = offset = si->cluster_next;
+       /*
+        * Use percpu scan base for SSD to reduce lock contention on
+        * cluster and swap cache.  For HDD, sequential access is more
+        * important.
+        */
+       if (si->flags & SWP_SOLIDSTATE)
+               scan_base = this_cpu_read(*si->cluster_next_cpu);
+       else
+               scan_base = si->cluster_next;
+       offset = scan_base;
 
        /* SSD algorithm */
        if (si->cluster_info) {
-               if (scan_swap_map_try_ssd_cluster(si, &offset, &scan_base))
-                       goto checks;
-               else
+               if (!scan_swap_map_try_ssd_cluster(si, &offset, &scan_base))
                        goto scan;
-       }
-
-       if (unlikely(!si->cluster_nr--)) {
+       } else if (unlikely(!si->cluster_nr--)) {
                if (si->pages - si->inuse_pages < SWAPFILE_CLUSTER) {
                        si->cluster_nr = SWAPFILE_CLUSTER - 1;
                        goto checks;
@@ -848,7 +874,6 @@ checks:
        unlock_cluster(ci);
 
        swap_range_alloc(si, offset, 1);
-       si->cluster_next = offset + 1;
        slots[n_ret++] = swp_entry(si->type, offset);
 
        /* got enough slots or reach max slots? */
@@ -871,19 +896,33 @@ checks:
        if (si->cluster_info) {
                if (scan_swap_map_try_ssd_cluster(si, &offset, &scan_base))
                        goto checks;
-               else
-                       goto done;
-       }
-       /* non-ssd case */
-       ++offset;
-
-       /* non-ssd case, still more slots in cluster? */
-       if (si->cluster_nr && !si->swap_map[offset]) {
+       } else if (si->cluster_nr && !si->swap_map[++offset]) {
+               /* non-ssd case, still more slots in cluster? */
                --si->cluster_nr;
                goto checks;
        }
 
+       /*
+        * Even if there's no free clusters available (fragmented),
+        * try to scan a little more quickly with lock held unless we
+        * have scanned too many slots already.
+        */
+       if (!scanned_many) {
+               unsigned long scan_limit;
+
+               if (offset < scan_base)
+                       scan_limit = scan_base;
+               else
+                       scan_limit = si->highest_bit;
+               for (; offset <= scan_limit && --latency_ration > 0;
+                    offset++) {
+                       if (!si->swap_map[offset])
+                               goto checks;
+               }
+       }
+
 done:
+       set_cluster_next(si, offset + 1);
        si->flags -= SWP_SCANNING;
        return n_ret;
 
@@ -901,6 +940,7 @@ scan:
                if (unlikely(--latency_ration < 0)) {
                        cond_resched();
                        latency_ration = LATENCY_LIMIT;
+                       scanned_many = true;
                }
        }
        offset = si->lowest_bit;
@@ -916,6 +956,7 @@ scan:
                if (unlikely(--latency_ration < 0)) {
                        cond_resched();
                        latency_ration = LATENCY_LIMIT;
+                       scanned_many = true;
                }
                offset++;
        }
@@ -1004,11 +1045,7 @@ int get_swap_pages(int n_goal, swp_entry_t swp_entries[], int entry_size)
        if (avail_pgs <= 0)
                goto noswap;
 
-       if (n_goal > SWAP_BATCH)
-               n_goal = SWAP_BATCH;
-
-       if (n_goal > avail_pgs)
-               n_goal = avail_pgs;
+       n_goal = min3((long)n_goal, (long)SWAP_BATCH, avail_pgs);
 
        atomic_long_sub(n_goal * size, &nr_swap_pages);
 
@@ -1275,13 +1312,14 @@ unlock_out:
 }
 
 static unsigned char __swap_entry_free(struct swap_info_struct *p,
-                                      swp_entry_t entry, unsigned char usage)
+                                      swp_entry_t entry)
 {
        struct swap_cluster_info *ci;
        unsigned long offset = swp_offset(entry);
+       unsigned char usage;
 
        ci = lock_cluster_or_swap_info(p, offset);
-       usage = __swap_entry_free_locked(p, offset, usage);
+       usage = __swap_entry_free_locked(p, offset, 1);
        unlock_cluster_or_swap_info(p, ci);
        if (!usage)
                free_swap_slot(entry);
@@ -1316,7 +1354,7 @@ void swap_free(swp_entry_t entry)
 
        p = _swap_info_get(entry);
        if (p)
-               __swap_entry_free(p, entry, 1);
+               __swap_entry_free(p, entry);
 }
 
 /*
@@ -1739,7 +1777,7 @@ int free_swap_and_cache(swp_entry_t entry)
 
        p = _swap_info_get(entry);
        if (p) {
-               count = __swap_entry_free(p, entry, 1);
+               count = __swap_entry_free(p, entry);
                if (count == SWAP_HAS_CACHE &&
                    !swap_page_trans_huge_swapped(p, entry))
                        __try_to_reclaim_swap(p, swp_offset(entry),
@@ -1937,10 +1975,14 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
 
                pte_unmap(pte);
                swap_map = &si->swap_map[offset];
-               vmf.vma = vma;
-               vmf.address = addr;
-               vmf.pmd = pmd;
-               page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, &vmf);
+               page = lookup_swap_cache(entry, vma, addr);
+               if (!page) {
+                       vmf.vma = vma;
+                       vmf.address = addr;
+                       vmf.pmd = pmd;
+                       page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE,
+                                               &vmf);
+               }
                if (!page) {
                        if (*swap_map == 0 || *swap_map == SWAP_MAP_BAD)
                                goto try_next;
@@ -2650,6 +2692,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
        mutex_unlock(&swapon_mutex);
        free_percpu(p->percpu_cluster);
        p->percpu_cluster = NULL;
+       free_percpu(p->cluster_next_cpu);
+       p->cluster_next_cpu = NULL;
        vfree(swap_map);
        kvfree(cluster_info);
        kvfree(frontswap_map);
@@ -2757,20 +2801,24 @@ static int swap_show(struct seq_file *swap, void *v)
        struct swap_info_struct *si = v;
        struct file *file;
        int len;
+       unsigned int bytes, inuse;
 
        if (si == SEQ_START_TOKEN) {
-               seq_puts(swap,"Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
+               seq_puts(swap,"Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n");
                return 0;
        }
 
+       bytes = si->pages << (PAGE_SHIFT - 10);
+       inuse = si->inuse_pages << (PAGE_SHIFT - 10);
+
        file = si->swap_file;
        len = seq_file_path(swap, file, " \t\n\\");
-       seq_printf(swap, "%*s%s\t%u\t%u\t%d\n",
+       seq_printf(swap, "%*s%s\t%u\t%s%u\t%s%d\n",
                        len < 40 ? 40 - len : 1, " ",
                        S_ISBLK(file_inode(file)->i_mode) ?
                                "partition" : "file\t",
-                       si->pages << (PAGE_SHIFT - 10),
-                       si->inuse_pages << (PAGE_SHIFT - 10),
+                       bytes, bytes < 10000000 ? "\t" : "",
+                       inuse, inuse < 10000000 ? "\t" : "",
                        si->prio);
        return 0;
 }
@@ -3202,11 +3250,19 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
                unsigned long ci, nr_cluster;
 
                p->flags |= SWP_SOLIDSTATE;
+               p->cluster_next_cpu = alloc_percpu(unsigned int);
+               if (!p->cluster_next_cpu) {
+                       error = -ENOMEM;
+                       goto bad_swap_unlock_inode;
+               }
                /*
                 * select a random position to start with to help wear leveling
                 * SSD
                 */
-               p->cluster_next = 1 + (prandom_u32() % p->highest_bit);
+               for_each_possible_cpu(cpu) {
+                       per_cpu(*p->cluster_next_cpu, cpu) =
+                               1 + prandom_u32_max(p->highest_bit);
+               }
                nr_cluster = DIV_ROUND_UP(maxpages, SWAPFILE_CLUSTER);
 
                cluster_info = kvcalloc(nr_cluster, sizeof(*cluster_info),
@@ -3322,6 +3378,8 @@ bad_swap_unlock_inode:
 bad_swap:
        free_percpu(p->percpu_cluster);
        p->percpu_cluster = NULL;
+       free_percpu(p->cluster_next_cpu);
+       p->cluster_next_cpu = NULL;
        if (inode && S_ISBLK(inode->i_mode) && p->bdev) {
                set_blocksize(p->bdev, p->old_block_size);
                blkdev_put(p->bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
@@ -3654,7 +3712,7 @@ static bool swap_count_continued(struct swap_info_struct *si,
 
        spin_lock(&si->cont_lock);
        offset &= ~PAGE_MASK;
-       page = list_entry(head->lru.next, struct page, lru);
+       page = list_next_entry(head, lru);
        map = kmap_atomic(page) + offset;
 
        if (count == SWAP_MAP_MAX)      /* initial increment from swap_map */
@@ -3666,13 +3724,13 @@ static bool swap_count_continued(struct swap_info_struct *si,
                 */
                while (*map == (SWAP_CONT_MAX | COUNT_CONTINUED)) {
                        kunmap_atomic(map);
-                       page = list_entry(page->lru.next, struct page, lru);
+                       page = list_next_entry(page, lru);
                        BUG_ON(page == head);
                        map = kmap_atomic(page) + offset;
                }
                if (*map == SWAP_CONT_MAX) {
                        kunmap_atomic(map);
-                       page = list_entry(page->lru.next, struct page, lru);
+                       page = list_next_entry(page, lru);
                        if (page == head) {
                                ret = false;    /* add count continuation */
                                goto out;
@@ -3682,12 +3740,10 @@ init_map:               *map = 0;               /* we didn't zero the page */
                }
                *map += 1;
                kunmap_atomic(map);
-               page = list_entry(page->lru.prev, struct page, lru);
-               while (page != head) {
+               while ((page = list_prev_entry(page, lru)) != head) {
                        map = kmap_atomic(page) + offset;
                        *map = COUNT_CONTINUED;
                        kunmap_atomic(map);
-                       page = list_entry(page->lru.prev, struct page, lru);
                }
                ret = true;                     /* incremented */
 
@@ -3698,7 +3754,7 @@ init_map:         *map = 0;               /* we didn't zero the page */
                BUG_ON(count != COUNT_CONTINUED);
                while (*map == COUNT_CONTINUED) {
                        kunmap_atomic(map);
-                       page = list_entry(page->lru.next, struct page, lru);
+                       page = list_next_entry(page, lru);
                        BUG_ON(page == head);
                        map = kmap_atomic(page) + offset;
                }
@@ -3707,13 +3763,11 @@ init_map:               *map = 0;               /* we didn't zero the page */
                if (*map == 0)
                        count = 0;
                kunmap_atomic(map);
-               page = list_entry(page->lru.prev, struct page, lru);
-               while (page != head) {
+               while ((page = list_prev_entry(page, lru)) != head) {
                        map = kmap_atomic(page) + offset;
                        *map = SWAP_CONT_MAX | count;
                        count = COUNT_CONTINUED;
                        kunmap_atomic(map);
-                       page = list_entry(page->lru.prev, struct page, lru);
                }
                ret = count == COUNT_CONTINUED;
        }
index 988d11e6c17cbc1cb8b0004efafc63a9cd10b2c7..6d5868adbe18301977bc7255734b6e03c7a34cc9 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -580,7 +580,7 @@ void *kvmalloc_node(size_t size, gfp_t flags, int node)
        if (ret || size <= PAGE_SIZE)
                return ret;
 
-       return __vmalloc_node_flags_caller(size, node, flags,
+       return __vmalloc_node(size, 1, flags, node,
                        __builtin_return_address(0));
 }
 EXPORT_SYMBOL(kvmalloc_node);
index 9a8227afa0738ffa87d3604a28788141d1abdc9a..1e94497b738853a08d383878fa5e8261dba18a26 100644 (file)
@@ -69,7 +69,8 @@ static void free_work(struct work_struct *w)
 
 /*** Page table manipulation functions ***/
 
-static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end)
+static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
+                            pgtbl_mod_mask *mask)
 {
        pte_t *pte;
 
@@ -78,73 +79,118 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end)
                pte_t ptent = ptep_get_and_clear(&init_mm, addr, pte);
                WARN_ON(!pte_none(ptent) && !pte_present(ptent));
        } while (pte++, addr += PAGE_SIZE, addr != end);
+       *mask |= PGTBL_PTE_MODIFIED;
 }
 
-static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end)
+static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
+                            pgtbl_mod_mask *mask)
 {
        pmd_t *pmd;
        unsigned long next;
+       int cleared;
 
        pmd = pmd_offset(pud, addr);
        do {
                next = pmd_addr_end(addr, end);
-               if (pmd_clear_huge(pmd))
+
+               cleared = pmd_clear_huge(pmd);
+               if (cleared || pmd_bad(*pmd))
+                       *mask |= PGTBL_PMD_MODIFIED;
+
+               if (cleared)
                        continue;
                if (pmd_none_or_clear_bad(pmd))
                        continue;
-               vunmap_pte_range(pmd, addr, next);
+               vunmap_pte_range(pmd, addr, next, mask);
        } while (pmd++, addr = next, addr != end);
 }
 
-static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end)
+static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
+                            pgtbl_mod_mask *mask)
 {
        pud_t *pud;
        unsigned long next;
+       int cleared;
 
        pud = pud_offset(p4d, addr);
        do {
                next = pud_addr_end(addr, end);
-               if (pud_clear_huge(pud))
+
+               cleared = pud_clear_huge(pud);
+               if (cleared || pud_bad(*pud))
+                       *mask |= PGTBL_PUD_MODIFIED;
+
+               if (cleared)
                        continue;
                if (pud_none_or_clear_bad(pud))
                        continue;
-               vunmap_pmd_range(pud, addr, next);
+               vunmap_pmd_range(pud, addr, next, mask);
        } while (pud++, addr = next, addr != end);
 }
 
-static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end)
+static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
+                            pgtbl_mod_mask *mask)
 {
        p4d_t *p4d;
        unsigned long next;
+       int cleared;
 
        p4d = p4d_offset(pgd, addr);
        do {
                next = p4d_addr_end(addr, end);
-               if (p4d_clear_huge(p4d))
+
+               cleared = p4d_clear_huge(p4d);
+               if (cleared || p4d_bad(*p4d))
+                       *mask |= PGTBL_P4D_MODIFIED;
+
+               if (cleared)
                        continue;
                if (p4d_none_or_clear_bad(p4d))
                        continue;
-               vunmap_pud_range(p4d, addr, next);
+               vunmap_pud_range(p4d, addr, next, mask);
        } while (p4d++, addr = next, addr != end);
 }
 
-static void vunmap_page_range(unsigned long addr, unsigned long end)
+/**
+ * unmap_kernel_range_noflush - unmap kernel VM area
+ * @start: start of the VM area to unmap
+ * @size: size of the VM area to unmap
+ *
+ * Unmap PFN_UP(@size) pages at @addr.  The VM area @addr and @size specify
+ * should have been allocated using get_vm_area() and its friends.
+ *
+ * NOTE:
+ * This function does NOT do any cache flushing.  The caller is responsible
+ * for calling flush_cache_vunmap() on to-be-mapped areas before calling this
+ * function and flush_tlb_kernel_range() after.
+ */
+void unmap_kernel_range_noflush(unsigned long start, unsigned long size)
 {
-       pgd_t *pgd;
+       unsigned long end = start + size;
        unsigned long next;
+       pgd_t *pgd;
+       unsigned long addr = start;
+       pgtbl_mod_mask mask = 0;
 
        BUG_ON(addr >= end);
+       start = addr;
        pgd = pgd_offset_k(addr);
        do {
                next = pgd_addr_end(addr, end);
+               if (pgd_bad(*pgd))
+                       mask |= PGTBL_PGD_MODIFIED;
                if (pgd_none_or_clear_bad(pgd))
                        continue;
-               vunmap_p4d_range(pgd, addr, next);
+               vunmap_p4d_range(pgd, addr, next, &mask);
        } while (pgd++, addr = next, addr != end);
+
+       if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
+               arch_sync_kernel_mappings(start, end);
 }
 
 static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
-               unsigned long end, pgprot_t prot, struct page **pages, int *nr)
+               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
+               pgtbl_mod_mask *mask)
 {
        pte_t *pte;
 
@@ -153,7 +199,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
         * callers keep track of where we're up to.
         */
 
-       pte = pte_alloc_kernel(pmd, addr);
+       pte = pte_alloc_kernel_track(pmd, addr, mask);
        if (!pte)
                return -ENOMEM;
        do {
@@ -166,94 +212,117 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
                set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));
                (*nr)++;
        } while (pte++, addr += PAGE_SIZE, addr != end);
+       *mask |= PGTBL_PTE_MODIFIED;
        return 0;
 }
 
 static int vmap_pmd_range(pud_t *pud, unsigned long addr,
-               unsigned long end, pgprot_t prot, struct page **pages, int *nr)
+               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
+               pgtbl_mod_mask *mask)
 {
        pmd_t *pmd;
        unsigned long next;
 
-       pmd = pmd_alloc(&init_mm, pud, addr);
+       pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
        if (!pmd)
                return -ENOMEM;
        do {
                next = pmd_addr_end(addr, end);
-               if (vmap_pte_range(pmd, addr, next, prot, pages, nr))
+               if (vmap_pte_range(pmd, addr, next, prot, pages, nr, mask))
                        return -ENOMEM;
        } while (pmd++, addr = next, addr != end);
        return 0;
 }
 
 static int vmap_pud_range(p4d_t *p4d, unsigned long addr,
-               unsigned long end, pgprot_t prot, struct page **pages, int *nr)
+               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
+               pgtbl_mod_mask *mask)
 {
        pud_t *pud;
        unsigned long next;
 
-       pud = pud_alloc(&init_mm, p4d, addr);
+       pud = pud_alloc_track(&init_mm, p4d, addr, mask);
        if (!pud)
                return -ENOMEM;
        do {
                next = pud_addr_end(addr, end);
-               if (vmap_pmd_range(pud, addr, next, prot, pages, nr))
+               if (vmap_pmd_range(pud, addr, next, prot, pages, nr, mask))
                        return -ENOMEM;
        } while (pud++, addr = next, addr != end);
        return 0;
 }
 
 static int vmap_p4d_range(pgd_t *pgd, unsigned long addr,
-               unsigned long end, pgprot_t prot, struct page **pages, int *nr)
+               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
+               pgtbl_mod_mask *mask)
 {
        p4d_t *p4d;
        unsigned long next;
 
-       p4d = p4d_alloc(&init_mm, pgd, addr);
+       p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
        if (!p4d)
                return -ENOMEM;
        do {
                next = p4d_addr_end(addr, end);
-               if (vmap_pud_range(p4d, addr, next, prot, pages, nr))
+               if (vmap_pud_range(p4d, addr, next, prot, pages, nr, mask))
                        return -ENOMEM;
        } while (p4d++, addr = next, addr != end);
        return 0;
 }
 
-/*
- * Set up page tables in kva (addr, end). The ptes shall have prot "prot", and
- * will have pfns corresponding to the "pages" array.
+/**
+ * map_kernel_range_noflush - map kernel VM area with the specified pages
+ * @addr: start of the VM area to map
+ * @size: size of the VM area to map
+ * @prot: page protection flags to use
+ * @pages: pages to map
  *
- * Ie. pte at addr+N*PAGE_SIZE shall point to pfn corresponding to pages[N]
+ * Map PFN_UP(@size) pages at @addr.  The VM area @addr and @size specify should
+ * have been allocated using get_vm_area() and its friends.
+ *
+ * NOTE:
+ * This function does NOT do any cache flushing.  The caller is responsible for
+ * calling flush_cache_vmap() on to-be-mapped areas before calling this
+ * function.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
  */
-static int vmap_page_range_noflush(unsigned long start, unsigned long end,
-                                  pgprot_t prot, struct page **pages)
+int map_kernel_range_noflush(unsigned long addr, unsigned long size,
+                            pgprot_t prot, struct page **pages)
 {
-       pgd_t *pgd;
+       unsigned long start = addr;
+       unsigned long end = addr + size;
        unsigned long next;
-       unsigned long addr = start;
+       pgd_t *pgd;
        int err = 0;
        int nr = 0;
+       pgtbl_mod_mask mask = 0;
 
        BUG_ON(addr >= end);
        pgd = pgd_offset_k(addr);
        do {
                next = pgd_addr_end(addr, end);
-               err = vmap_p4d_range(pgd, addr, next, prot, pages, &nr);
+               if (pgd_bad(*pgd))
+                       mask |= PGTBL_PGD_MODIFIED;
+               err = vmap_p4d_range(pgd, addr, next, prot, pages, &nr, &mask);
                if (err)
                        return err;
        } while (pgd++, addr = next, addr != end);
 
-       return nr;
+       if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
+               arch_sync_kernel_mappings(start, end);
+
+       return 0;
 }
 
-static int vmap_page_range(unsigned long start, unsigned long end,
-                          pgprot_t prot, struct page **pages)
+int map_kernel_range(unsigned long start, unsigned long size, pgprot_t prot,
+               struct page **pages)
 {
        int ret;
 
-       ret = vmap_page_range_noflush(start, end, prot, pages);
-       flush_cache_vmap(start, end);
+       ret = map_kernel_range_noflush(start, size, prot, pages);
+       flush_cache_vmap(start, start + size);
        return ret;
 }
 
@@ -1222,14 +1291,6 @@ int unregister_vmap_purge_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(unregister_vmap_purge_notifier);
 
-/*
- * Clear the pagetable entries of a given vmap_area
- */
-static void unmap_vmap_area(struct vmap_area *va)
-{
-       vunmap_page_range(va->va_start, va->va_end);
-}
-
 /*
  * lazy_max_pages is the maximum amount of virtual address space we gather up
  * before attempting to purge with a TLB flush.
@@ -1292,12 +1353,6 @@ static bool __purge_vmap_area_lazy(unsigned long start, unsigned long end)
        if (unlikely(valist == NULL))
                return false;
 
-       /*
-        * First make sure the mappings are removed from all page-tables
-        * before they are freed.
-        */
-       vmalloc_sync_unmappings();
-
        /*
         * TODO: to calculate a flush range without looping.
         * The list can be up to lazy_max_pages() elements.
@@ -1391,7 +1446,7 @@ static void free_vmap_area_noflush(struct vmap_area *va)
 static void free_unmap_vmap_area(struct vmap_area *va)
 {
        flush_cache_vunmap(va->va_start, va->va_end);
-       unmap_vmap_area(va);
+       unmap_kernel_range_noflush(va->va_start, va->va_end - va->va_start);
        if (debug_pagealloc_enabled_static())
                flush_tlb_kernel_range(va->va_start, va->va_end);
 
@@ -1665,7 +1720,7 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask)
        return vaddr;
 }
 
-static void vb_free(const void *addr, unsigned long size)
+static void vb_free(unsigned long addr, unsigned long size)
 {
        unsigned long offset;
        unsigned long vb_idx;
@@ -1675,24 +1730,22 @@ static void vb_free(const void *addr, unsigned long size)
        BUG_ON(offset_in_page(size));
        BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
 
-       flush_cache_vunmap((unsigned long)addr, (unsigned long)addr + size);
+       flush_cache_vunmap(addr, addr + size);
 
        order = get_order(size);
 
-       offset = (unsigned long)addr & (VMAP_BLOCK_SIZE - 1);
-       offset >>= PAGE_SHIFT;
+       offset = (addr & (VMAP_BLOCK_SIZE - 1)) >> PAGE_SHIFT;
 
-       vb_idx = addr_to_vb_idx((unsigned long)addr);
+       vb_idx = addr_to_vb_idx(addr);
        rcu_read_lock();
        vb = radix_tree_lookup(&vmap_block_tree, vb_idx);
        rcu_read_unlock();
        BUG_ON(!vb);
 
-       vunmap_page_range((unsigned long)addr, (unsigned long)addr + size);
+       unmap_kernel_range_noflush(addr, size);
 
        if (debug_pagealloc_enabled_static())
-               flush_tlb_kernel_range((unsigned long)addr,
-                                       (unsigned long)addr + size);
+               flush_tlb_kernel_range(addr, addr + size);
 
        spin_lock(&vb->lock);
 
@@ -1792,7 +1845,7 @@ void vm_unmap_ram(const void *mem, unsigned int count)
 
        if (likely(count <= VMAP_MAX_ALLOC)) {
                debug_check_no_locks_freed(mem, size);
-               vb_free(mem, size);
+               vb_free(addr, size);
                return;
        }
 
@@ -1819,7 +1872,7 @@ EXPORT_SYMBOL(vm_unmap_ram);
  *
  * Returns: a pointer to the address that has been mapped, or %NULL on failure
  */
-void *vm_map_ram(struct page **pages, unsigned int count, int node, pgprot_t prot)
+void *vm_map_ram(struct page **pages, unsigned int count, int node)
 {
        unsigned long size = (unsigned long)count << PAGE_SHIFT;
        unsigned long addr;
@@ -1843,7 +1896,7 @@ void *vm_map_ram(struct page **pages, unsigned int count, int node, pgprot_t pro
 
        kasan_unpoison_vmalloc(mem, size);
 
-       if (vmap_page_range(addr, addr + size, prot, pages) < 0) {
+       if (map_kernel_range(addr, size, PAGE_KERNEL, pages) < 0) {
                vm_unmap_ram(mem, count);
                return NULL;
        }
@@ -1987,51 +2040,6 @@ void __init vmalloc_init(void)
        vmap_initialized = true;
 }
 
-/**
- * map_kernel_range_noflush - map kernel VM area with the specified pages
- * @addr: start of the VM area to map
- * @size: size of the VM area to map
- * @prot: page protection flags to use
- * @pages: pages to map
- *
- * Map PFN_UP(@size) pages at @addr.  The VM area @addr and @size
- * specify should have been allocated using get_vm_area() and its
- * friends.
- *
- * NOTE:
- * This function does NOT do any cache flushing.  The caller is
- * responsible for calling flush_cache_vmap() on to-be-mapped areas
- * before calling this function.
- *
- * RETURNS:
- * The number of pages mapped on success, -errno on failure.
- */
-int map_kernel_range_noflush(unsigned long addr, unsigned long size,
-                            pgprot_t prot, struct page **pages)
-{
-       return vmap_page_range_noflush(addr, addr + size, prot, pages);
-}
-
-/**
- * unmap_kernel_range_noflush - unmap kernel VM area
- * @addr: start of the VM area to unmap
- * @size: size of the VM area to unmap
- *
- * Unmap PFN_UP(@size) pages at @addr.  The VM area @addr and @size
- * specify should have been allocated using get_vm_area() and its
- * friends.
- *
- * NOTE:
- * This function does NOT do any cache flushing.  The caller is
- * responsible for calling flush_cache_vunmap() on to-be-mapped areas
- * before calling this function and flush_tlb_kernel_range() after.
- */
-void unmap_kernel_range_noflush(unsigned long addr, unsigned long size)
-{
-       vunmap_page_range(addr, addr + size);
-}
-EXPORT_SYMBOL_GPL(unmap_kernel_range_noflush);
-
 /**
  * unmap_kernel_range - unmap kernel VM area and flush cache and TLB
  * @addr: start of the VM area to unmap
@@ -2045,22 +2053,9 @@ void unmap_kernel_range(unsigned long addr, unsigned long size)
        unsigned long end = addr + size;
 
        flush_cache_vunmap(addr, end);
-       vunmap_page_range(addr, end);
+       unmap_kernel_range_noflush(addr, size);
        flush_tlb_kernel_range(addr, end);
 }
-EXPORT_SYMBOL_GPL(unmap_kernel_range);
-
-int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page **pages)
-{
-       unsigned long addr = (unsigned long)area->addr;
-       unsigned long end = addr + get_vm_area_size(area);
-       int err;
-
-       err = vmap_page_range(addr, end, prot, pages);
-
-       return err > 0 ? 0 : err;
-}
-EXPORT_SYMBOL_GPL(map_vm_area);
 
 static inline void setup_vmalloc_vm_locked(struct vm_struct *vm,
        struct vmap_area *va, unsigned long flags, const void *caller)
@@ -2128,14 +2123,6 @@ static struct vm_struct *__get_vm_area_node(unsigned long size,
        return area;
 }
 
-struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
-                               unsigned long start, unsigned long end)
-{
-       return __get_vm_area_node(size, 1, flags, start, end, NUMA_NO_NODE,
-                                 GFP_KERNEL, __builtin_return_address(0));
-}
-EXPORT_SYMBOL_GPL(__get_vm_area);
-
 struct vm_struct *__get_vm_area_caller(unsigned long size, unsigned long flags,
                                       unsigned long start, unsigned long end,
                                       const void *caller)
@@ -2441,7 +2428,8 @@ void *vmap(struct page **pages, unsigned int count,
        if (!area)
                return NULL;
 
-       if (map_vm_area(area, prot, pages)) {
+       if (map_kernel_range((unsigned long)area->addr, size, pgprot_nx(prot),
+                       pages) < 0) {
                vunmap(area->addr);
                return NULL;
        }
@@ -2450,9 +2438,6 @@ void *vmap(struct page **pages, unsigned int count,
 }
 EXPORT_SYMBOL(vmap);
 
-static void *__vmalloc_node(unsigned long size, unsigned long align,
-                           gfp_t gfp_mask, pgprot_t prot,
-                           int node, const void *caller);
 static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
                                 pgprot_t prot, int node)
 {
@@ -2470,7 +2455,7 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
        /* Please note that the recursion is strictly bounded. */
        if (array_size > PAGE_SIZE) {
                pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
-                               PAGE_KERNEL, node, area->caller);
+                               node, area->caller);
        } else {
                pages = kmalloc_node(array_size, nested_gfp, node);
        }
@@ -2504,8 +2489,10 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
        }
        atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
 
-       if (map_vm_area(area, prot, pages))
+       if (map_kernel_range((unsigned long)area->addr, get_vm_area_size(area),
+                       prot, pages) < 0)
                goto fail;
+
        return area->addr;
 
 fail:
@@ -2573,27 +2560,16 @@ fail:
        return NULL;
 }
 
-/*
- * This is only for performance analysis of vmalloc and stress purpose.
- * It is required by vmalloc test module, therefore do not use it other
- * than that.
- */
-#ifdef CONFIG_TEST_VMALLOC_MODULE
-EXPORT_SYMBOL_GPL(__vmalloc_node_range);
-#endif
-
 /**
  * __vmalloc_node - allocate virtually contiguous memory
  * @size:          allocation size
  * @align:         desired alignment
  * @gfp_mask:      flags for the page level allocator
- * @prot:          protection mask for the allocated pages
  * @node:          node to use for allocation or NUMA_NO_NODE
  * @caller:        caller's return address
  *
- * Allocate enough pages to cover @size from the page level
- * allocator with @gfp_mask flags.  Map them into contiguous
- * kernel virtual space, using a pagetable protection of @prot.
+ * Allocate enough pages to cover @size from the page level allocator with
+ * @gfp_mask flags.  Map them into contiguous kernel virtual space.
  *
  * Reclaim modifiers in @gfp_mask - __GFP_NORETRY, __GFP_RETRY_MAYFAIL
  * and __GFP_NOFAIL are not supported
@@ -2603,35 +2579,28 @@ EXPORT_SYMBOL_GPL(__vmalloc_node_range);
  *
  * Return: pointer to the allocated memory or %NULL on error
  */
-static void *__vmalloc_node(unsigned long size, unsigned long align,
-                           gfp_t gfp_mask, pgprot_t prot,
-                           int node, const void *caller)
+void *__vmalloc_node(unsigned long size, unsigned long align,
+                           gfp_t gfp_mask, int node, const void *caller)
 {
        return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
-                               gfp_mask, prot, 0, node, caller);
+                               gfp_mask, PAGE_KERNEL, 0, node, caller);
 }
+/*
+ * This is only for performance analysis of vmalloc and stress purpose.
+ * It is required by vmalloc test module, therefore do not use it other
+ * than that.
+ */
+#ifdef CONFIG_TEST_VMALLOC_MODULE
+EXPORT_SYMBOL_GPL(__vmalloc_node);
+#endif
 
-void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
+void *__vmalloc(unsigned long size, gfp_t gfp_mask)
 {
-       return __vmalloc_node(size, 1, gfp_mask, prot, NUMA_NO_NODE,
+       return __vmalloc_node(size, 1, gfp_mask, NUMA_NO_NODE,
                                __builtin_return_address(0));
 }
 EXPORT_SYMBOL(__vmalloc);
 
-static inline void *__vmalloc_node_flags(unsigned long size,
-                                       int node, gfp_t flags)
-{
-       return __vmalloc_node(size, 1, flags, PAGE_KERNEL,
-                                       node, __builtin_return_address(0));
-}
-
-
-void *__vmalloc_node_flags_caller(unsigned long size, int node, gfp_t flags,
-                                 void *caller)
-{
-       return __vmalloc_node(size, 1, flags, PAGE_KERNEL, node, caller);
-}
-
 /**
  * vmalloc - allocate virtually contiguous memory
  * @size:    allocation size
@@ -2646,8 +2615,8 @@ void *__vmalloc_node_flags_caller(unsigned long size, int node, gfp_t flags,
  */
 void *vmalloc(unsigned long size)
 {
-       return __vmalloc_node_flags(size, NUMA_NO_NODE,
-                                   GFP_KERNEL);
+       return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE,
+                               __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vmalloc);
 
@@ -2666,8 +2635,8 @@ EXPORT_SYMBOL(vmalloc);
  */
 void *vzalloc(unsigned long size)
 {
-       return __vmalloc_node_flags(size, NUMA_NO_NODE,
-                               GFP_KERNEL | __GFP_ZERO);
+       return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, NUMA_NO_NODE,
+                               __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vzalloc);
 
@@ -2704,8 +2673,8 @@ EXPORT_SYMBOL(vmalloc_user);
  */
 void *vmalloc_node(unsigned long size, int node)
 {
-       return __vmalloc_node(size, 1, GFP_KERNEL, PAGE_KERNEL,
-                                       node, __builtin_return_address(0));
+       return __vmalloc_node(size, 1, GFP_KERNEL, node,
+                       __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vmalloc_node);
 
@@ -2718,38 +2687,15 @@ EXPORT_SYMBOL(vmalloc_node);
  * allocator and map them into contiguous kernel virtual space.
  * The memory allocated is set to zero.
  *
- * For tight control over page level allocator and protection flags
- * use __vmalloc_node() instead.
- *
  * Return: pointer to the allocated memory or %NULL on error
  */
 void *vzalloc_node(unsigned long size, int node)
 {
-       return __vmalloc_node_flags(size, node,
-                        GFP_KERNEL | __GFP_ZERO);
+       return __vmalloc_node(size, 1, GFP_KERNEL | __GFP_ZERO, node,
+                               __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vzalloc_node);
 
-/**
- * vmalloc_user_node_flags - allocate memory for userspace on a specific node
- * @size: allocation size
- * @node: numa node
- * @flags: flags for the page level allocator
- *
- * The resulting memory area is zeroed so it can be mapped to userspace
- * without leaking data.
- *
- * Return: pointer to the allocated memory or %NULL on error
- */
-void *vmalloc_user_node_flags(unsigned long size, int node, gfp_t flags)
-{
-       return __vmalloc_node_range(size, SHMLBA,  VMALLOC_START, VMALLOC_END,
-                                   flags | __GFP_ZERO, PAGE_KERNEL,
-                                   VM_USERMAP, node,
-                                   __builtin_return_address(0));
-}
-EXPORT_SYMBOL(vmalloc_user_node_flags);
-
 /**
  * vmalloc_exec - allocate virtually contiguous, executable memory
  * @size:        allocation size
@@ -2793,8 +2739,8 @@ void *vmalloc_exec(unsigned long size)
  */
 void *vmalloc_32(unsigned long size)
 {
-       return __vmalloc_node(size, 1, GFP_VMALLOC32, PAGE_KERNEL,
-                             NUMA_NO_NODE, __builtin_return_address(0));
+       return __vmalloc_node(size, 1, GFP_VMALLOC32, NUMA_NO_NODE,
+                       __builtin_return_address(0));
 }
 EXPORT_SYMBOL(vmalloc_32);
 
@@ -3137,21 +3083,6 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
 }
 EXPORT_SYMBOL(remap_vmalloc_range);
 
-/*
- * Implement stubs for vmalloc_sync_[un]mappings () if the architecture chose
- * not to have one.
- *
- * The purpose of this function is to make sure the vmalloc area
- * mappings are identical in all page-tables in the system.
- */
-void __weak vmalloc_sync_mappings(void)
-{
-}
-
-void __weak vmalloc_sync_unmappings(void)
-{
-}
-
 static int f(pte_t *pte, unsigned long addr, void *data)
 {
        pte_t ***p = data;
index a37c87b5aee2f3c038da7920ef8df6ee7de415d2..b2f5deb3603c8a716ecd5c5c4532f56754d0b887 100644 (file)
@@ -1878,13 +1878,13 @@ static unsigned noinline_for_stack move_pages_to_lru(struct lruvec *lruvec,
 
 /*
  * If a kernel thread (such as nfsd for loop-back mounts) services
- * a backing device by writing to the page cache it sets PF_LESS_THROTTLE.
+ * a backing device by writing to the page cache it sets PF_LOCAL_THROTTLE.
  * In that case we should only throttle if the backing device it is
  * writing to is congested.  In other cases it is safe to throttle.
  */
 static int current_may_throttle(void)
 {
-       return !(current->flags & PF_LESS_THROTTLE) ||
+       return !(current->flags & PF_LOCAL_THROTTLE) ||
                current->backing_dev_info == NULL ||
                bdi_write_congested(current->backing_dev_info);
 }
index 96d21a792b57c35ad59f1f062b7efedf9f59a34c..5e241434cab28887d55f4f66a65a13656848c234 100644 (file)
@@ -1108,7 +1108,7 @@ int fragmentation_index(struct zone *zone, unsigned int order)
                                        TEXT_FOR_HIGHMEM(xx) xx "_movable",
 
 const char * const vmstat_text[] = {
-       /* enum zone_stat_item countes */
+       /* enum zone_stat_item counters */
        "nr_free_pages",
        "nr_zone_inactive_anon",
        "nr_zone_active_anon",
@@ -1119,6 +1119,9 @@ const char * const vmstat_text[] = {
        "nr_mlock",
        "nr_page_table_pages",
        "nr_kernel_stack",
+#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK)
+       "nr_shadow_call_stack",
+#endif
        "nr_bounce",
 #if IS_ENABLED(CONFIG_ZSMALLOC)
        "nr_zspages",
@@ -1162,7 +1165,6 @@ const char * const vmstat_text[] = {
        "nr_file_hugepages",
        "nr_file_pmdmapped",
        "nr_anon_transparent_hugepages",
-       "nr_unstable",
        "nr_vmscan_write",
        "nr_vmscan_immediate_reclaim",
        "nr_dirtied",
@@ -1723,6 +1725,14 @@ static int vmstat_show(struct seq_file *m, void *arg)
        seq_puts(m, vmstat_text[off]);
        seq_put_decimal_ull(m, " ", *l);
        seq_putc(m, '\n');
+
+       if (off == NR_VMSTAT_ITEMS - 1) {
+               /*
+                * We've come to the end - add any deprecated counters to avoid
+                * breaking userspace which might depend on them being present.
+                */
+               seq_puts(m, "nr_unstable 0\n");
+       }
        return 0;
 }
 
index 8c3bb5e508b84057468009ee4debc717bbaa59df..460b0feced26abe6d3b2cc16305fbf1c44635190 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/spinlock.h>
 #include <linux/zpool.h>
 #include <linux/magic.h>
+#include <linux/kmemleak.h>
 
 /*
  * NCHUNKS_ORDER determines the internal allocation granularity, effectively
@@ -215,6 +216,8 @@ static inline struct z3fold_buddy_slots *alloc_slots(struct z3fold_pool *pool,
                                 (gfp & ~(__GFP_HIGHMEM | __GFP_MOVABLE)));
 
        if (slots) {
+               /* It will be freed separately in free_handle(). */
+               kmemleak_not_leak(slots);
                memset(slots->slot, 0, sizeof(slots->slot));
                slots->pool = (unsigned long)pool;
                rwlock_init(&slots->lock);
index 2f836a2b993f0cf16a3cf2d6c28e8a52d30ba604..f6dc0673e62cf973f03602c8d065a117f5c14a2a 100644 (file)
@@ -293,7 +293,7 @@ struct zspage {
 };
 
 struct mapping_area {
-#ifdef CONFIG_PGTABLE_MAPPING
+#ifdef CONFIG_ZSMALLOC_PGTABLE_MAPPING
        struct vm_struct *vm; /* vm area for mapping object that span pages */
 #else
        char *vm_buf; /* copy buffer for objects that span pages */
@@ -1113,7 +1113,7 @@ static struct zspage *find_get_zspage(struct size_class *class)
        return zspage;
 }
 
-#ifdef CONFIG_PGTABLE_MAPPING
+#ifdef CONFIG_ZSMALLOC_PGTABLE_MAPPING
 static inline int __zs_cpu_up(struct mapping_area *area)
 {
        /*
@@ -1138,7 +1138,9 @@ static inline void __zs_cpu_down(struct mapping_area *area)
 static inline void *__zs_map_object(struct mapping_area *area,
                                struct page *pages[2], int off, int size)
 {
-       BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, pages));
+       unsigned long addr = (unsigned long)area->vm->addr;
+
+       BUG_ON(map_kernel_range(addr, PAGE_SIZE * 2, PAGE_KERNEL, pages) < 0);
        area->vm_addr = area->vm->addr;
        return area->vm_addr + off;
 }
@@ -1151,7 +1153,7 @@ static inline void __zs_unmap_object(struct mapping_area *area,
        unmap_kernel_range(addr, PAGE_SIZE * 2);
 }
 
-#else /* CONFIG_PGTABLE_MAPPING */
+#else /* CONFIG_ZSMALLOC_PGTABLE_MAPPING */
 
 static inline int __zs_cpu_up(struct mapping_area *area)
 {
@@ -1233,7 +1235,7 @@ out:
        pagefault_enable();
 }
 
-#endif /* CONFIG_PGTABLE_MAPPING */
+#endif /* CONFIG_ZSMALLOC_PGTABLE_MAPPING */
 
 static int zs_cpu_prepare(unsigned int cpu)
 {
index 1476a91ce935725e06d4979d96fad1a6b036a811..d022f126eb026b2caaa3ead84f6c3131b6d45cfe 100644 (file)
@@ -170,7 +170,6 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
                    size_t len, u8 mac[16])
 {
        uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
-       SHASH_DESC_ON_STACK(desc, tfm);
        int err;
 
        if (len > CMAC_MSG_MAX)
@@ -181,8 +180,6 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
                return -EINVAL;
        }
 
-       desc->tfm = tfm;
-
        /* Swap key and message from LSB to MSB */
        swap_buf(k, tmp, 16);
        swap_buf(m, msg_msb, len);
@@ -196,8 +193,7 @@ static int aes_cmac(struct crypto_shash *tfm, const u8 k[16], const u8 *m,
                return err;
        }
 
-       err = crypto_shash_digest(desc, msg_msb, len, mac_msb);
-       shash_desc_zero(desc);
+       err = crypto_shash_tfm_digest(tfm, msg_msb, len, mac_msb);
        if (err) {
                BT_ERR("Hash computation error %d", err);
                return err;
index ad12fe3fca8cfb4888e500b11cd6ade9fb67ed53..83490bf73a13b3f2e0f19d83eab6e780ee8b7204 100644 (file)
@@ -2413,7 +2413,8 @@ void br_multicast_uninit_stats(struct net_bridge *br)
        free_percpu(br->mcast_stats);
 }
 
-static void mcast_stats_add_dir(u64 *dst, u64 *src)
+/* noinline for https://bugs.llvm.org/show_bug.cgi?id=45802#c9 */
+static noinline_for_stack void mcast_stats_add_dir(u64 *dst, u64 *src)
 {
        dst[BR_MCAST_DIR_RX] += src[BR_MCAST_DIR_RX];
        dst[BR_MCAST_DIR_TX] += src[BR_MCAST_DIR_TX];
index 78db58c7aec2eafffa174ffa4c503c68f13bc24c..7e869284e0523e04d6828388710fdb49424dbb74 100644 (file)
@@ -1095,16 +1095,14 @@ static int do_replace(struct net *net, const void __user *user,
        tmp.name[sizeof(tmp.name) - 1] = 0;
 
        countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
-       newinfo = __vmalloc(sizeof(*newinfo) + countersize, GFP_KERNEL_ACCOUNT,
-                           PAGE_KERNEL);
+       newinfo = __vmalloc(sizeof(*newinfo) + countersize, GFP_KERNEL_ACCOUNT);
        if (!newinfo)
                return -ENOMEM;
 
        if (countersize)
                memset(newinfo->counters, 0, countersize);
 
-       newinfo->entries = __vmalloc(tmp.entries_size, GFP_KERNEL_ACCOUNT,
-                                    PAGE_KERNEL);
+       newinfo->entries = __vmalloc(tmp.entries_size, GFP_KERNEL_ACCOUNT);
        if (!newinfo->entries) {
                ret = -ENOMEM;
                goto free_newinfo;
index b325b569e76155f70d2266fb1aba3b8074537b91..f48cf4cfb80f9e4e1981dbb28a97e80d6af50176 100644 (file)
@@ -31,6 +31,12 @@ static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
        ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source);
        eth->h_proto = eth_hdr(oldskb)->h_proto;
        skb_pull(nskb, ETH_HLEN);
+
+       if (skb_vlan_tag_present(oldskb)) {
+               u16 vid = skb_vlan_tag_get(oldskb);
+
+               __vlan_hwaccel_put_tag(nskb, oldskb->vlan_proto, vid);
+       }
 }
 
 static int nft_bridge_iphdr_validate(struct sk_buff *skb)
index a0e97f6c1072afc592339edfb2d71f66d308b5e8..66f22e8aa5296840cb9329e23febc5981def5951 100644 (file)
@@ -190,8 +190,7 @@ EXPORT_SYMBOL(ceph_compare_options);
  * kvmalloc() doesn't fall back to the vmalloc allocator unless flags are
  * compatible with (a superset of) GFP_KERNEL.  This is because while the
  * actual pages are allocated with the specified flags, the page table pages
- * are always allocated with GFP_KERNEL.  map_vm_area() doesn't even take
- * flags because GFP_KERNEL is hard-coded in {p4d,pud,pmd,pte}_alloc().
+ * are always allocated with GFP_KERNEL.
  *
  * ceph_kvmalloc() may be called with GFP_KERNEL, GFP_NOFS or GFP_NOIO.
  */
index 998e26b75a789bcdca78c1ce3af4c9f2e734a9a4..1d4973f8cd7a69efe0763ba18990f96a2108893a 100644 (file)
@@ -3649,7 +3649,9 @@ static void handle_reply(struct ceph_osd *osd, struct ceph_msg *msg)
                 * supported.
                 */
                req->r_t.target_oloc.pool = m.redirect.oloc.pool;
-               req->r_flags |= CEPH_OSD_FLAG_REDIRECTED;
+               req->r_flags |= CEPH_OSD_FLAG_REDIRECTED |
+                               CEPH_OSD_FLAG_IGNORE_OVERLAY |
+                               CEPH_OSD_FLAG_IGNORE_CACHE;
                req->r_tid = 0;
                __submit_request(req, false);
                goto out_unlock_osdc;
index 116139233d573c1b15969d4a9a9b1b08f2e930bb..dbe0c6ead773f5909c55ce73281a19e5a99f94f1 100644 (file)
@@ -1082,8 +1082,8 @@ static void neigh_timer_handler(struct timer_list *t)
        }
 
        if (neigh->nud_state & NUD_IN_TIMER) {
-               if (time_before(next, jiffies + HZ/2))
-                       next = jiffies + HZ/2;
+               if (time_before(next, jiffies + HZ/100))
+                       next = jiffies + HZ/100;
                if (!mod_timer(&neigh->timer, next))
                        neigh_hold(neigh);
        }
index 7b6b1d2c3d10913ef1ce35c3b67dd76d195273e5..b5bc680d475536de6da68a9a8815691cf81176a6 100644 (file)
@@ -5,7 +5,6 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/cryptohash.h>
 #include <linux/module.h>
 #include <linux/cache.h>
 #include <linux/random.h>
index 62f4ee3da172a0489a183e3e9abd6f1439ad1481..d3bcb9afa795ae09660bb59915eb7bcd8b5b9a60 100644 (file)
@@ -1736,6 +1736,7 @@ int dsa_slave_create(struct dsa_port *port)
        if (ds->ops->port_vlan_add && ds->ops->port_vlan_del)
                slave_dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        slave_dev->hw_features |= NETIF_F_HW_TC;
+       slave_dev->features |= NETIF_F_LLTX;
        slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
        if (!IS_ERR_OR_NULL(port->mac))
                ether_addr_copy(slave_dev->dev_addr, port->mac);
index c0dd561aa19032f8b6637d9b387e4fa404b31828..5267b6b191ebaa540b0012d468fd995d3c79cd8f 100644 (file)
@@ -276,6 +276,7 @@ static struct in_device *inetdev_init(struct net_device *dev)
        err = devinet_sysctl_register(in_dev);
        if (err) {
                in_dev->dead = 1;
+               neigh_parms_release(&arp_tbl, in_dev->arp_parms);
                in_dev_put(in_dev);
                in_dev = NULL;
                goto out;
index 731022cff6006ef900b4e62bd5f0f27fc934d40a..d14133eac4763580b98b5e4d5575403036ced783 100644 (file)
@@ -63,10 +63,8 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
                sp->olen++;
 
                xo = xfrm_offload(skb);
-               if (!xo) {
-                       xfrm_state_put(x);
+               if (!xo)
                        goto out_reset;
-               }
        }
 
        xo->flags |= XFRM_GRO;
@@ -139,19 +137,27 @@ static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x,
        struct xfrm_offload *xo = xfrm_offload(skb);
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        const struct net_offload *ops;
-       int proto = xo->proto;
+       u8 proto = xo->proto;
 
        skb->transport_header += x->props.header_len;
 
-       if (proto == IPPROTO_BEETPH) {
-               struct ip_beet_phdr *ph = (struct ip_beet_phdr *)skb->data;
+       if (x->sel.family != AF_INET6) {
+               if (proto == IPPROTO_BEETPH) {
+                       struct ip_beet_phdr *ph =
+                               (struct ip_beet_phdr *)skb->data;
+
+                       skb->transport_header += ph->hdrlen * 8;
+                       proto = ph->nexthdr;
+               } else {
+                       skb->transport_header -= IPV4_BEET_PHMAXLEN;
+               }
+       } else {
+               __be16 frag;
 
-               skb->transport_header += ph->hdrlen * 8;
-               proto = ph->nexthdr;
-       } else if (x->sel.family != AF_INET6) {
-               skb->transport_header -= IPV4_BEET_PHMAXLEN;
-       } else if (proto == IPPROTO_TCP) {
-               skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
+               skb->transport_header +=
+                       ipv6_skip_exthdr(skb, 0, &proto, &frag);
+               if (proto == IPPROTO_TCP)
+                       skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
        }
 
        __skb_pull(skb, skb_transport_offset(skb));
index 1bf9da3a75f921738b28f17cc1543fbf84993845..41079490a118162e992e44e272b72f3b688093c9 100644 (file)
@@ -309,17 +309,18 @@ bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev)
 {
        bool dev_match = false;
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
-       int ret;
+       if (unlikely(fi->nh)) {
+               dev_match = nexthop_uses_dev(fi->nh, dev);
+       } else {
+               int ret;
 
-       for (ret = 0; ret < fib_info_num_path(fi); ret++) {
-               const struct fib_nh_common *nhc = fib_info_nhc(fi, ret);
+               for (ret = 0; ret < fib_info_num_path(fi); ret++) {
+                       const struct fib_nh_common *nhc = fib_info_nhc(fi, ret);
 
-               if (nhc->nhc_dev == dev) {
-                       dev_match = true;
-                       break;
-               } else if (l3mdev_master_ifindex_rcu(nhc->nhc_dev) == dev->ifindex) {
-                       dev_match = true;
-                       break;
+                       if (nhc_l3mdev_matches_dev(nhc, dev)) {
+                               dev_match = true;
+                               break;
+                       }
                }
        }
 #else
index 4f334b42553853b2cc1de001bd99a53a71de5030..248f1c1959a63c4aa22331d2a937aa5aecaf3f0e 100644 (file)
@@ -1371,6 +1371,26 @@ static inline t_key prefix_mismatch(t_key key, struct key_vector *n)
        return (key ^ prefix) & (prefix | -prefix);
 }
 
+bool fib_lookup_good_nhc(const struct fib_nh_common *nhc, int fib_flags,
+                        const struct flowi4 *flp)
+{
+       if (nhc->nhc_flags & RTNH_F_DEAD)
+               return false;
+
+       if (ip_ignore_linkdown(nhc->nhc_dev) &&
+           nhc->nhc_flags & RTNH_F_LINKDOWN &&
+           !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE))
+               return false;
+
+       if (!(flp->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) {
+               if (flp->flowi4_oif &&
+                   flp->flowi4_oif != nhc->nhc_oif)
+                       return false;
+       }
+
+       return true;
+}
+
 /* should be called with rcu_read_lock */
 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
                     struct fib_result *res, int fib_flags)
@@ -1503,6 +1523,7 @@ found:
        /* Step 3: Process the leaf, if that fails fall back to backtracing */
        hlist_for_each_entry_rcu(fa, &n->leaf, fa_list) {
                struct fib_info *fi = fa->fa_info;
+               struct fib_nh_common *nhc;
                int nhsel, err;
 
                if ((BITS_PER_LONG > KEYLENGTH) || (fa->fa_slen < KEYLENGTH)) {
@@ -1528,26 +1549,25 @@ out_reject:
                if (fi->fib_flags & RTNH_F_DEAD)
                        continue;
 
-               if (unlikely(fi->nh && nexthop_is_blackhole(fi->nh))) {
-                       err = fib_props[RTN_BLACKHOLE].error;
-                       goto out_reject;
+               if (unlikely(fi->nh)) {
+                       if (nexthop_is_blackhole(fi->nh)) {
+                               err = fib_props[RTN_BLACKHOLE].error;
+                               goto out_reject;
+                       }
+
+                       nhc = nexthop_get_nhc_lookup(fi->nh, fib_flags, flp,
+                                                    &nhsel);
+                       if (nhc)
+                               goto set_result;
+                       goto miss;
                }
 
                for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) {
-                       struct fib_nh_common *nhc = fib_info_nhc(fi, nhsel);
+                       nhc = fib_info_nhc(fi, nhsel);
 
-                       if (nhc->nhc_flags & RTNH_F_DEAD)
+                       if (!fib_lookup_good_nhc(nhc, fib_flags, flp))
                                continue;
-                       if (ip_ignore_linkdown(nhc->nhc_dev) &&
-                           nhc->nhc_flags & RTNH_F_LINKDOWN &&
-                           !(fib_flags & FIB_LOOKUP_IGNORE_LINKSTATE))
-                               continue;
-                       if (!(flp->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF)) {
-                               if (flp->flowi4_oif &&
-                                   flp->flowi4_oif != nhc->nhc_oif)
-                                       continue;
-                       }
-
+set_result:
                        if (!(fib_flags & FIB_LOOKUP_NOREF))
                                refcount_inc(&fi->fib_clntref);
 
@@ -1568,6 +1588,7 @@ out_reject:
                        return err;
                }
        }
+miss:
 #ifdef CONFIG_IP_FIB_TRIE_STATS
        this_cpu_inc(stats->semantic_match_miss);
 #endif
index 1b4e6f298648d9cdce5b482a7ba25ae4733bfea6..1dda7c155c484da6995b363a2a6fff441b202a31 100644 (file)
@@ -93,7 +93,28 @@ static int vti_rcv_proto(struct sk_buff *skb)
 
 static int vti_rcv_tunnel(struct sk_buff *skb)
 {
-       return vti_rcv(skb, ip_hdr(skb)->saddr, true);
+       struct ip_tunnel_net *itn = net_generic(dev_net(skb->dev), vti_net_id);
+       const struct iphdr *iph = ip_hdr(skb);
+       struct ip_tunnel *tunnel;
+
+       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
+                                 iph->saddr, iph->daddr, 0);
+       if (tunnel) {
+               struct tnl_ptk_info tpi = {
+                       .proto = htons(ETH_P_IP),
+               };
+
+               if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+                       goto drop;
+               if (iptunnel_pull_header(skb, 0, tpi.proto, false))
+                       goto drop;
+               return ip_tunnel_rcv(tunnel, skb, &tpi, NULL, false);
+       }
+
+       return -EINVAL;
+drop:
+       kfree_skb(skb);
+       return 0;
 }
 
 static int vti_rcv_cb(struct sk_buff *skb, int err)
index 3c25a467b3efc9c408ca6f51f622675c81ee4710..7afde8828b4c9c3f17f6ae3beea8d7f7d0b677eb 100644 (file)
@@ -166,8 +166,7 @@ pptp_outbound_pkt(struct sk_buff *skb,
                break;
        default:
                pr_debug("unknown outbound packet 0x%04x:%s\n", msg,
-                        msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] :
-                                              pptp_msg_name[0]);
+                        pptp_msg_name(msg));
                fallthrough;
        case PPTP_SET_LINK_INFO:
                /* only need to NAT in case PAC is behind NAT box */
@@ -268,9 +267,7 @@ pptp_inbound_pkt(struct sk_buff *skb,
                pcid_off = offsetof(union pptp_ctrl_union, setlink.peersCallID);
                break;
        default:
-               pr_debug("unknown inbound packet %s\n",
-                        msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] :
-                                              pptp_msg_name[0]);
+               pr_debug("unknown inbound packet %s\n", pptp_msg_name(msg));
                fallthrough;
        case PPTP_START_SESSION_REQUEST:
        case PPTP_START_SESSION_REPLY:
index 715e14475220f1766ad9434db7d07c0e6b146f88..563f71bcb2d74764fc2b1379bdc75d0c2c9141cf 100644 (file)
@@ -63,9 +63,16 @@ static void nexthop_free_mpath(struct nexthop *nh)
        int i;
 
        nhg = rcu_dereference_raw(nh->nh_grp);
-       for (i = 0; i < nhg->num_nh; ++i)
-               WARN_ON(nhg->nh_entries[i].nh);
+       for (i = 0; i < nhg->num_nh; ++i) {
+               struct nh_grp_entry *nhge = &nhg->nh_entries[i];
+
+               WARN_ON(!list_empty(&nhge->nh_list));
+               nexthop_put(nhge->nh);
+       }
+
+       WARN_ON(nhg->spare == nhg);
 
+       kfree(nhg->spare);
        kfree(nhg);
 }
 
@@ -694,41 +701,56 @@ static void nh_group_rebalance(struct nh_group *nhg)
        }
 }
 
-static void remove_nh_grp_entry(struct nh_grp_entry *nhge,
-                               struct nh_group *nhg,
+static void remove_nh_grp_entry(struct net *net, struct nh_grp_entry *nhge,
                                struct nl_info *nlinfo)
 {
+       struct nh_grp_entry *nhges, *new_nhges;
+       struct nexthop *nhp = nhge->nh_parent;
        struct nexthop *nh = nhge->nh;
-       struct nh_grp_entry *nhges;
-       bool found = false;
-       int i;
+       struct nh_group *nhg, *newg;
+       int i, j;
 
        WARN_ON(!nh);
 
-       nhges = nhg->nh_entries;
-       for (i = 0; i < nhg->num_nh; ++i) {
-               if (found) {
-                       nhges[i-1].nh = nhges[i].nh;
-                       nhges[i-1].weight = nhges[i].weight;
-                       list_del(&nhges[i].nh_list);
-                       list_add(&nhges[i-1].nh_list, &nhges[i-1].nh->grp_list);
-               } else if (nhg->nh_entries[i].nh == nh) {
-                       found = true;
-               }
-       }
+       nhg = rtnl_dereference(nhp->nh_grp);
+       newg = nhg->spare;
 
-       if (WARN_ON(!found))
+       /* last entry, keep it visible and remove the parent */
+       if (nhg->num_nh == 1) {
+               remove_nexthop(net, nhp, nlinfo);
                return;
+       }
+
+       newg->has_v4 = nhg->has_v4;
+       newg->mpath = nhg->mpath;
+       newg->num_nh = nhg->num_nh;
 
-       nhg->num_nh--;
-       nhg->nh_entries[nhg->num_nh].nh = NULL;
+       /* copy old entries to new except the one getting removed */
+       nhges = nhg->nh_entries;
+       new_nhges = newg->nh_entries;
+       for (i = 0, j = 0; i < nhg->num_nh; ++i) {
+               /* current nexthop getting removed */
+               if (nhg->nh_entries[i].nh == nh) {
+                       newg->num_nh--;
+                       continue;
+               }
 
-       nh_group_rebalance(nhg);
+               list_del(&nhges[i].nh_list);
+               new_nhges[j].nh_parent = nhges[i].nh_parent;
+               new_nhges[j].nh = nhges[i].nh;
+               new_nhges[j].weight = nhges[i].weight;
+               list_add(&new_nhges[j].nh_list, &new_nhges[j].nh->grp_list);
+               j++;
+       }
 
-       nexthop_put(nh);
+       nh_group_rebalance(newg);
+       rcu_assign_pointer(nhp->nh_grp, newg);
+
+       list_del(&nhge->nh_list);
+       nexthop_put(nhge->nh);
 
        if (nlinfo)
-               nexthop_notify(RTM_NEWNEXTHOP, nhge->nh_parent, nlinfo);
+               nexthop_notify(RTM_NEWNEXTHOP, nhp, nlinfo);
 }
 
 static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
@@ -736,17 +758,11 @@ static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
 {
        struct nh_grp_entry *nhge, *tmp;
 
-       list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list) {
-               struct nh_group *nhg;
-
-               list_del(&nhge->nh_list);
-               nhg = rtnl_dereference(nhge->nh_parent->nh_grp);
-               remove_nh_grp_entry(nhge, nhg, nlinfo);
+       list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list)
+               remove_nh_grp_entry(net, nhge, nlinfo);
 
-               /* if this group has no more entries then remove it */
-               if (!nhg->num_nh)
-                       remove_nexthop(net, nhge->nh_parent, nlinfo);
-       }
+       /* make sure all see the newly published array before releasing rtnl */
+       synchronize_rcu();
 }
 
 static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo)
@@ -760,10 +776,7 @@ static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo)
                if (WARN_ON(!nhge->nh))
                        continue;
 
-               list_del(&nhge->nh_list);
-               nexthop_put(nhge->nh);
-               nhge->nh = NULL;
-               nhg->num_nh--;
+               list_del_init(&nhge->nh_list);
        }
 }
 
@@ -1086,6 +1099,7 @@ static struct nexthop *nexthop_create_group(struct net *net,
 {
        struct nlattr *grps_attr = cfg->nh_grp;
        struct nexthop_grp *entry = nla_data(grps_attr);
+       u16 num_nh = nla_len(grps_attr) / sizeof(*entry);
        struct nh_group *nhg;
        struct nexthop *nh;
        int i;
@@ -1096,12 +1110,21 @@ static struct nexthop *nexthop_create_group(struct net *net,
 
        nh->is_group = 1;
 
-       nhg = nexthop_grp_alloc(nla_len(grps_attr) / sizeof(*entry));
+       nhg = nexthop_grp_alloc(num_nh);
        if (!nhg) {
                kfree(nh);
                return ERR_PTR(-ENOMEM);
        }
 
+       /* spare group used for removals */
+       nhg->spare = nexthop_grp_alloc(num_nh);
+       if (!nhg) {
+               kfree(nhg);
+               kfree(nh);
+               return NULL;
+       }
+       nhg->spare->spare = nhg;
+
        for (i = 0; i < nhg->num_nh; ++i) {
                struct nexthop *nhe;
                struct nh_info *nhi;
@@ -1133,6 +1156,7 @@ out_no_nh:
        for (; i >= 0; --i)
                nexthop_put(nhg->nh_entries[i].nh);
 
+       kfree(nhg->spare);
        kfree(nhg);
        kfree(nh);
 
index 24e319dfb5103d88097d1afa0a0675655ec1836d..f131cedf5ba677d94f42d9466fd8afcef6a951e8 100644 (file)
@@ -3222,11 +3222,11 @@ static int ipv6_generate_stable_address(struct in6_addr *address,
                                        const struct inet6_dev *idev)
 {
        static DEFINE_SPINLOCK(lock);
-       static __u32 digest[SHA_DIGEST_WORDS];
-       static __u32 workspace[SHA_WORKSPACE_WORDS];
+       static __u32 digest[SHA1_DIGEST_WORDS];
+       static __u32 workspace[SHA1_WORKSPACE_WORDS];
 
        static union {
-               char __data[SHA_MESSAGE_BYTES];
+               char __data[SHA1_BLOCK_SIZE];
                struct {
                        struct in6_addr secret;
                        __be32 prefix[2];
@@ -3251,7 +3251,7 @@ static int ipv6_generate_stable_address(struct in6_addr *address,
 retry:
        spin_lock_bh(&lock);
 
-       sha_init(digest);
+       sha1_init(digest);
        memset(&data, 0, sizeof(data));
        memset(workspace, 0, sizeof(workspace));
        memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
@@ -3260,7 +3260,7 @@ retry:
        data.secret = secret;
        data.dad_count = dad_count;
 
-       sha_transform(digest, data.__data, workspace);
+       sha1_transform(digest, data.__data, workspace);
 
        temp = *address;
        temp.s6_addr32[2] = (__force __be32)digest[0];
index 8eab2c869d6154e212a2630976c4e6d468457ab2..ab0eea336c70d35f73faf05f457986a31da3fb61 100644 (file)
@@ -85,10 +85,8 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
                sp->olen++;
 
                xo = xfrm_offload(skb);
-               if (!xo) {
-                       xfrm_state_put(x);
+               if (!xo)
                        goto out_reset;
-               }
        }
 
        xo->flags |= XFRM_GRO;
@@ -123,9 +121,16 @@ static void esp6_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
        struct ip_esp_hdr *esph;
        struct ipv6hdr *iph = ipv6_hdr(skb);
        struct xfrm_offload *xo = xfrm_offload(skb);
-       int proto = iph->nexthdr;
+       u8 proto = iph->nexthdr;
 
        skb_push(skb, -skb_network_offset(skb));
+
+       if (x->outer_mode.encap == XFRM_MODE_TRANSPORT) {
+               __be16 frag;
+
+               ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &proto, &frag);
+       }
+
        esph = ip_esp_hdr(skb);
        *skb_mac_header(skb) = IPPROTO_ESP;
 
@@ -166,23 +171,31 @@ static struct sk_buff *xfrm6_beet_gso_segment(struct xfrm_state *x,
        struct xfrm_offload *xo = xfrm_offload(skb);
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        const struct net_offload *ops;
-       int proto = xo->proto;
+       u8 proto = xo->proto;
 
        skb->transport_header += x->props.header_len;
 
-       if (proto == IPPROTO_BEETPH) {
-               struct ip_beet_phdr *ph = (struct ip_beet_phdr *)skb->data;
-
-               skb->transport_header += ph->hdrlen * 8;
-               proto = ph->nexthdr;
-       }
-
        if (x->sel.family != AF_INET6) {
                skb->transport_header -=
                        (sizeof(struct ipv6hdr) - sizeof(struct iphdr));
 
+               if (proto == IPPROTO_BEETPH) {
+                       struct ip_beet_phdr *ph =
+                               (struct ip_beet_phdr *)skb->data;
+
+                       skb->transport_header += ph->hdrlen * 8;
+                       proto = ph->nexthdr;
+               } else {
+                       skb->transport_header -= IPV4_BEET_PHMAXLEN;
+               }
+
                if (proto == IPPROTO_TCP)
                        skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
+       } else {
+               __be16 frag;
+
+               skb->transport_header +=
+                       ipv6_skip_exthdr(skb, 0, &proto, &frag);
        }
 
        __skb_pull(skb, skb_transport_offset(skb));
index ffcfcd2b128f3ae9c6e6964d3e36d95014828858..85dddfe3a2c6ed61fc152be01946e818f4e250a9 100644 (file)
@@ -34,7 +34,6 @@
 #include <net/addrconf.h>
 #include <net/xfrm.h>
 
-#include <linux/cryptohash.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
 #include <net/seg6.h>
index fcb53ed1c4fb98de3d60c52542e4c4260582bf3a..6d7ef78c88af059a4cbfb5d89f32ad6d1babfe74 100644 (file)
@@ -1458,6 +1458,9 @@ static int l2tp_validate_socket(const struct sock *sk, const struct net *net,
        if (sk->sk_type != SOCK_DGRAM)
                return -EPROTONOSUPPORT;
 
+       if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+               return -EPROTONOSUPPORT;
+
        if ((encap == L2TP_ENCAPTYPE_UDP && sk->sk_protocol != IPPROTO_UDP) ||
            (encap == L2TP_ENCAPTYPE_IP && sk->sk_protocol != IPPROTO_L2TP))
                return -EPROTONOSUPPORT;
index 0d7c887a2b75db65afba7955a2bf9572a6a37786..955662a6dee754478da0f8ac95d41a787339242b 100644 (file)
@@ -20,7 +20,6 @@
 #include <net/icmp.h>
 #include <net/udp.h>
 #include <net/inet_common.h>
-#include <net/inet_hashtables.h>
 #include <net/tcp_states.h>
 #include <net/protocol.h>
 #include <net/xfrm.h>
@@ -209,15 +208,31 @@ discard:
        return 0;
 }
 
-static int l2tp_ip_open(struct sock *sk)
+static int l2tp_ip_hash(struct sock *sk)
 {
-       /* Prevent autobind. We don't have ports. */
-       inet_sk(sk)->inet_num = IPPROTO_L2TP;
+       if (sk_unhashed(sk)) {
+               write_lock_bh(&l2tp_ip_lock);
+               sk_add_node(sk, &l2tp_ip_table);
+               write_unlock_bh(&l2tp_ip_lock);
+       }
+       return 0;
+}
 
+static void l2tp_ip_unhash(struct sock *sk)
+{
+       if (sk_unhashed(sk))
+               return;
        write_lock_bh(&l2tp_ip_lock);
-       sk_add_node(sk, &l2tp_ip_table);
+       sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip_lock);
+}
+
+static int l2tp_ip_open(struct sock *sk)
+{
+       /* Prevent autobind. We don't have ports. */
+       inet_sk(sk)->inet_num = IPPROTO_L2TP;
 
+       l2tp_ip_hash(sk);
        return 0;
 }
 
@@ -594,8 +609,8 @@ static struct proto l2tp_ip_prot = {
        .sendmsg           = l2tp_ip_sendmsg,
        .recvmsg           = l2tp_ip_recvmsg,
        .backlog_rcv       = l2tp_ip_backlog_recv,
-       .hash              = inet_hash,
-       .unhash            = inet_unhash,
+       .hash              = l2tp_ip_hash,
+       .unhash            = l2tp_ip_unhash,
        .obj_size          = sizeof(struct l2tp_ip_sock),
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_ip_setsockopt,
index d148766f40d117c50fc28092173d3686428d1dfc..0fa694bd3f6a992518cab05feb8922fbf94d9829 100644 (file)
@@ -20,8 +20,6 @@
 #include <net/icmp.h>
 #include <net/udp.h>
 #include <net/inet_common.h>
-#include <net/inet_hashtables.h>
-#include <net/inet6_hashtables.h>
 #include <net/tcp_states.h>
 #include <net/protocol.h>
 #include <net/xfrm.h>
@@ -222,15 +220,31 @@ discard:
        return 0;
 }
 
-static int l2tp_ip6_open(struct sock *sk)
+static int l2tp_ip6_hash(struct sock *sk)
 {
-       /* Prevent autobind. We don't have ports. */
-       inet_sk(sk)->inet_num = IPPROTO_L2TP;
+       if (sk_unhashed(sk)) {
+               write_lock_bh(&l2tp_ip6_lock);
+               sk_add_node(sk, &l2tp_ip6_table);
+               write_unlock_bh(&l2tp_ip6_lock);
+       }
+       return 0;
+}
 
+static void l2tp_ip6_unhash(struct sock *sk)
+{
+       if (sk_unhashed(sk))
+               return;
        write_lock_bh(&l2tp_ip6_lock);
-       sk_add_node(sk, &l2tp_ip6_table);
+       sk_del_node_init(sk);
        write_unlock_bh(&l2tp_ip6_lock);
+}
+
+static int l2tp_ip6_open(struct sock *sk)
+{
+       /* Prevent autobind. We don't have ports. */
+       inet_sk(sk)->inet_num = IPPROTO_L2TP;
 
+       l2tp_ip6_hash(sk);
        return 0;
 }
 
@@ -728,8 +742,8 @@ static struct proto l2tp_ip6_prot = {
        .sendmsg           = l2tp_ip6_sendmsg,
        .recvmsg           = l2tp_ip6_recvmsg,
        .backlog_rcv       = l2tp_ip6_backlog_recv,
-       .hash              = inet6_hash,
-       .unhash            = inet_unhash,
+       .hash              = l2tp_ip6_hash,
+       .unhash            = l2tp_ip6_unhash,
        .obj_size          = sizeof(struct l2tp_ip6_sock),
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_ipv6_setsockopt,
index 38a0383dfbcfaee991f9bbc05cc5d5dfabd76a90..aa5150929996d609ccf8075465e12c70dbd9fc18 100644 (file)
@@ -1103,7 +1103,14 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
        mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, ifmsh->sn,
                               target_flags, mpath->dst, mpath->sn, da, 0,
                               ttl, lifetime, 0, ifmsh->preq_id++, sdata);
+
+       spin_lock_bh(&mpath->state_lock);
+       if (mpath->flags & MESH_PATH_DELETED) {
+               spin_unlock_bh(&mpath->state_lock);
+               goto enddiscovery;
+       }
        mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
+       spin_unlock_bh(&mpath->state_lock);
 
 enddiscovery:
        rcu_read_unlock();
index 0f5a414a936632d16a4647df26472a735611e5ce..3d980713a9e273a47b791435a1613be0e4617c58 100644 (file)
@@ -59,7 +59,7 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac)
        put_unaligned_be64(key2, key2be);
 
        /* Generate key xored with ipad */
-       memset(input, 0x36, SHA_MESSAGE_BYTES);
+       memset(input, 0x36, SHA256_BLOCK_SIZE);
        for (i = 0; i < 8; i++)
                input[i] ^= key1be[i];
        for (i = 0; i < 8; i++)
@@ -76,7 +76,7 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac)
        sha256_final(&state, &input[SHA256_BLOCK_SIZE]);
 
        /* Prepare second part of hmac */
-       memset(input, 0x5C, SHA_MESSAGE_BYTES);
+       memset(input, 0x5C, SHA256_BLOCK_SIZE);
        for (i = 0; i < 8; i++)
                input[i] ^= key1be[i];
        for (i = 0; i < 8; i++)
index 32ea8d35489a51a3a65b130cad74a3ea88ed6cac..34dd0e278a82976eac7e7f9ba8ffe6cf8062c78b 100644 (file)
@@ -954,7 +954,8 @@ fallback:
 
                pr_debug("block timeout %ld", timeo);
                mptcp_wait_data(sk, &timeo);
-               if (unlikely(__mptcp_tcp_fallback(msk)))
+               ssock = __mptcp_tcp_fallback(msk);
+               if (unlikely(ssock))
                        goto fallback;
        }
 
@@ -1262,11 +1263,14 @@ static void mptcp_close(struct sock *sk, long timeout)
 
        lock_sock(sk);
 
-       mptcp_token_destroy(msk->token);
        inet_sk_state_store(sk, TCP_CLOSE);
 
-       __mptcp_flush_join_list(msk);
-
+       /* be sure to always acquire the join list lock, to sync vs
+        * mptcp_finish_join().
+        */
+       spin_lock_bh(&msk->join_list_lock);
+       list_splice_tail_init(&msk->join_list, &msk->conn_list);
+       spin_unlock_bh(&msk->join_list_lock);
        list_splice_init(&msk->conn_list, &conn_list);
 
        data_fin_tx_seq = msk->write_seq;
@@ -1456,6 +1460,7 @@ static void mptcp_destroy(struct sock *sk)
 {
        struct mptcp_sock *msk = mptcp_sk(sk);
 
+       mptcp_token_destroy(msk->token);
        if (msk->cached_ext)
                __skb_ext_put(msk->cached_ext);
 
@@ -1622,22 +1627,30 @@ bool mptcp_finish_join(struct sock *sk)
        if (!msk->pm.server_side)
                return true;
 
-       /* passive connection, attach to msk socket */
+       if (!mptcp_pm_allow_new_subflow(msk))
+               return false;
+
+       /* active connections are already on conn_list, and we can't acquire
+        * msk lock here.
+        * use the join list lock as synchronization point and double-check
+        * msk status to avoid racing with mptcp_close()
+        */
+       spin_lock_bh(&msk->join_list_lock);
+       ret = inet_sk_state_load(parent) == TCP_ESTABLISHED;
+       if (ret && !WARN_ON_ONCE(!list_empty(&subflow->node)))
+               list_add_tail(&subflow->node, &msk->join_list);
+       spin_unlock_bh(&msk->join_list_lock);
+       if (!ret)
+               return false;
+
+       /* attach to msk socket only after we are sure he will deal with us
+        * at close time
+        */
        parent_sock = READ_ONCE(parent->sk_socket);
        if (parent_sock && !sk->sk_socket)
                mptcp_sock_graft(sk, parent_sock);
-
-       ret = mptcp_pm_allow_new_subflow(msk);
-       if (ret) {
-               subflow->map_seq = msk->ack_seq;
-
-               /* active connections are already on conn_list */
-               spin_lock_bh(&msk->join_list_lock);
-               if (!WARN_ON_ONCE(!list_empty(&subflow->node)))
-                       list_add_tail(&subflow->node, &msk->join_list);
-               spin_unlock_bh(&msk->join_list_lock);
-       }
-       return ret;
+       subflow->map_seq = msk->ack_seq;
+       return true;
 }
 
 bool mptcp_sk_is_subflow(const struct sock *sk)
@@ -1711,6 +1724,14 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr,
        int err;
 
        lock_sock(sock->sk);
+       if (sock->state != SS_UNCONNECTED && msk->subflow) {
+               /* pending connection or invalid state, let existing subflow
+                * cope with that
+                */
+               ssock = msk->subflow;
+               goto do_connect;
+       }
+
        ssock = __mptcp_socket_create(msk, TCP_SYN_SENT);
        if (IS_ERR(ssock)) {
                err = PTR_ERR(ssock);
@@ -1725,9 +1746,17 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr,
                mptcp_subflow_ctx(ssock->sk)->request_mptcp = 0;
 #endif
 
+do_connect:
        err = ssock->ops->connect(ssock, uaddr, addr_len, flags);
-       inet_sk_state_store(sock->sk, inet_sk_state_load(ssock->sk));
-       mptcp_copy_inaddrs(sock->sk, ssock->sk);
+       sock->state = ssock->state;
+
+       /* on successful connect, the msk state will be moved to established by
+        * subflow_finish_connect()
+        */
+       if (!err || err == EINPROGRESS)
+               mptcp_copy_inaddrs(sock->sk, ssock->sk);
+       else
+               inet_sk_state_store(sock->sk, inet_sk_state_load(ssock->sk));
 
 unlock:
        release_sock(sock->sk);
index cd747c0962fd609c738d51f913a42ef92e1b375b..5a67f79665742c3a2907baa7acab75cdec6ea32f 100644 (file)
@@ -59,7 +59,7 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
        /* Don't lookup sub-counters at all */
        opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
        if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
-               opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
+               opt->cmdflags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
        list_for_each_entry_rcu(e, &map->members, list) {
                ret = ip_set_test(e->id, skb, par, opt);
                if (ret <= 0)
index 1d57b95d348192226a16c6cdc92e1fe14c6d0c5e..bb72ca5f3999aa49b39aea824b6db1cfdb2dd46b 100644 (file)
@@ -2016,22 +2016,18 @@ static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb)
        nf_conntrack_get(skb_nfct(nskb));
 }
 
-static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
+static int __nf_conntrack_update(struct net *net, struct sk_buff *skb,
+                                struct nf_conn *ct,
+                                enum ip_conntrack_info ctinfo)
 {
        struct nf_conntrack_tuple_hash *h;
        struct nf_conntrack_tuple tuple;
-       enum ip_conntrack_info ctinfo;
        struct nf_nat_hook *nat_hook;
        unsigned int status;
-       struct nf_conn *ct;
        int dataoff;
        u16 l3num;
        u8 l4num;
 
-       ct = nf_ct_get(skb, &ctinfo);
-       if (!ct || nf_ct_is_confirmed(ct))
-               return 0;
-
        l3num = nf_ct_l3num(ct);
 
        dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num);
@@ -2088,6 +2084,76 @@ static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
        return 0;
 }
 
+/* This packet is coming from userspace via nf_queue, complete the packet
+ * processing after the helper invocation in nf_confirm().
+ */
+static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct,
+                              enum ip_conntrack_info ctinfo)
+{
+       const struct nf_conntrack_helper *helper;
+       const struct nf_conn_help *help;
+       int protoff;
+
+       help = nfct_help(ct);
+       if (!help)
+               return 0;
+
+       helper = rcu_dereference(help->helper);
+       if (!(helper->flags & NF_CT_HELPER_F_USERSPACE))
+               return 0;
+
+       switch (nf_ct_l3num(ct)) {
+       case NFPROTO_IPV4:
+               protoff = skb_network_offset(skb) + ip_hdrlen(skb);
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case NFPROTO_IPV6: {
+               __be16 frag_off;
+               u8 pnum;
+
+               pnum = ipv6_hdr(skb)->nexthdr;
+               protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
+                                          &frag_off);
+               if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
+                       return 0;
+               break;
+       }
+#endif
+       default:
+               return 0;
+       }
+
+       if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
+           !nf_is_loopback_packet(skb)) {
+               if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
+                       NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
+                       return -1;
+               }
+       }
+
+       /* We've seen it coming out the other side: confirm it */
+       return nf_conntrack_confirm(skb) == NF_DROP ? - 1 : 0;
+}
+
+static int nf_conntrack_update(struct net *net, struct sk_buff *skb)
+{
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct;
+       int err;
+
+       ct = nf_ct_get(skb, &ctinfo);
+       if (!ct)
+               return 0;
+
+       if (!nf_ct_is_confirmed(ct)) {
+               err = __nf_conntrack_update(net, skb, ct, ctinfo);
+               if (err < 0)
+                       return err;
+       }
+
+       return nf_confirm_cthelper(skb, ct, ctinfo);
+}
+
 static bool nf_conntrack_get_tuple_skb(struct nf_conntrack_tuple *dst_tuple,
                                       const struct sk_buff *skb)
 {
index a971183f11af77ac1533de77a7778adab37d4758..1f44d523b5121c84057f9f66dc902203090dbd57 100644 (file)
@@ -72,24 +72,32 @@ EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
 
 #if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
 /* PptpControlMessageType names */
-const char *const pptp_msg_name[] = {
-       "UNKNOWN_MESSAGE",
-       "START_SESSION_REQUEST",
-       "START_SESSION_REPLY",
-       "STOP_SESSION_REQUEST",
-       "STOP_SESSION_REPLY",
-       "ECHO_REQUEST",
-       "ECHO_REPLY",
-       "OUT_CALL_REQUEST",
-       "OUT_CALL_REPLY",
-       "IN_CALL_REQUEST",
-       "IN_CALL_REPLY",
-       "IN_CALL_CONNECT",
-       "CALL_CLEAR_REQUEST",
-       "CALL_DISCONNECT_NOTIFY",
-       "WAN_ERROR_NOTIFY",
-       "SET_LINK_INFO"
+static const char *const pptp_msg_name_array[PPTP_MSG_MAX + 1] = {
+       [0]                             = "UNKNOWN_MESSAGE",
+       [PPTP_START_SESSION_REQUEST]    = "START_SESSION_REQUEST",
+       [PPTP_START_SESSION_REPLY]      = "START_SESSION_REPLY",
+       [PPTP_STOP_SESSION_REQUEST]     = "STOP_SESSION_REQUEST",
+       [PPTP_STOP_SESSION_REPLY]       = "STOP_SESSION_REPLY",
+       [PPTP_ECHO_REQUEST]             = "ECHO_REQUEST",
+       [PPTP_ECHO_REPLY]               = "ECHO_REPLY",
+       [PPTP_OUT_CALL_REQUEST]         = "OUT_CALL_REQUEST",
+       [PPTP_OUT_CALL_REPLY]           = "OUT_CALL_REPLY",
+       [PPTP_IN_CALL_REQUEST]          = "IN_CALL_REQUEST",
+       [PPTP_IN_CALL_REPLY]            = "IN_CALL_REPLY",
+       [PPTP_IN_CALL_CONNECT]          = "IN_CALL_CONNECT",
+       [PPTP_CALL_CLEAR_REQUEST]       = "CALL_CLEAR_REQUEST",
+       [PPTP_CALL_DISCONNECT_NOTIFY]   = "CALL_DISCONNECT_NOTIFY",
+       [PPTP_WAN_ERROR_NOTIFY]         = "WAN_ERROR_NOTIFY",
+       [PPTP_SET_LINK_INFO]            = "SET_LINK_INFO"
 };
+
+const char *pptp_msg_name(u_int16_t msg)
+{
+       if (msg > PPTP_MSG_MAX)
+               return pptp_msg_name_array[0];
+
+       return pptp_msg_name_array[msg];
+}
 EXPORT_SYMBOL(pptp_msg_name);
 #endif
 
@@ -276,7 +284,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
        typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound;
 
        msg = ntohs(ctlh->messageType);
-       pr_debug("inbound control message %s\n", pptp_msg_name[msg]);
+       pr_debug("inbound control message %s\n", pptp_msg_name(msg));
 
        switch (msg) {
        case PPTP_START_SESSION_REPLY:
@@ -311,7 +319,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
                pcid = pptpReq->ocack.peersCallID;
                if (info->pns_call_id != pcid)
                        goto invalid;
-               pr_debug("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
+               pr_debug("%s, CID=%X, PCID=%X\n", pptp_msg_name(msg),
                         ntohs(cid), ntohs(pcid));
 
                if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) {
@@ -328,7 +336,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
                        goto invalid;
 
                cid = pptpReq->icreq.callID;
-               pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
+               pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
                info->cstate = PPTP_CALL_IN_REQ;
                info->pac_call_id = cid;
                break;
@@ -347,7 +355,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
                if (info->pns_call_id != pcid)
                        goto invalid;
 
-               pr_debug("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
+               pr_debug("%s, PCID=%X\n", pptp_msg_name(msg), ntohs(pcid));
                info->cstate = PPTP_CALL_IN_CONF;
 
                /* we expect a GRE connection from PAC to PNS */
@@ -357,7 +365,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
        case PPTP_CALL_DISCONNECT_NOTIFY:
                /* server confirms disconnect */
                cid = pptpReq->disc.callID;
-               pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
+               pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
                info->cstate = PPTP_CALL_NONE;
 
                /* untrack this call id, unexpect GRE packets */
@@ -384,7 +392,7 @@ pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff,
 invalid:
        pr_debug("invalid %s: type=%d cid=%u pcid=%u "
                 "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
-                msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
+                pptp_msg_name(msg),
                 msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
                 ntohs(info->pns_call_id), ntohs(info->pac_call_id));
        return NF_ACCEPT;
@@ -404,7 +412,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
        typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound;
 
        msg = ntohs(ctlh->messageType);
-       pr_debug("outbound control message %s\n", pptp_msg_name[msg]);
+       pr_debug("outbound control message %s\n", pptp_msg_name(msg));
 
        switch (msg) {
        case PPTP_START_SESSION_REQUEST:
@@ -426,7 +434,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
                info->cstate = PPTP_CALL_OUT_REQ;
                /* track PNS call id */
                cid = pptpReq->ocreq.callID;
-               pr_debug("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
+               pr_debug("%s, CID=%X\n", pptp_msg_name(msg), ntohs(cid));
                info->pns_call_id = cid;
                break;
 
@@ -440,7 +448,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
                pcid = pptpReq->icack.peersCallID;
                if (info->pac_call_id != pcid)
                        goto invalid;
-               pr_debug("%s, CID=%X PCID=%X\n", pptp_msg_name[msg],
+               pr_debug("%s, CID=%X PCID=%X\n", pptp_msg_name(msg),
                         ntohs(cid), ntohs(pcid));
 
                if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) {
@@ -480,7 +488,7 @@ pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff,
 invalid:
        pr_debug("invalid %s: type=%d cid=%u pcid=%u "
                 "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
-                msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
+                pptp_msg_name(msg),
                 msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
                 ntohs(info->pns_call_id), ntohs(info->pac_call_id));
        return NF_ACCEPT;
index a5f294aa8e4cf9c3ef361d775de6f8707d2f1143..5b0d0a77379c64bce6c55ba185022b95d1554ec6 100644 (file)
@@ -103,7 +103,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct)
        if (help->helper->data_len == 0)
                return -EINVAL;
 
-       nla_memcpy(help->data, nla_data(attr), sizeof(help->data));
+       nla_memcpy(help->data, attr, sizeof(help->data));
        return 0;
 }
 
@@ -240,6 +240,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
                ret = -ENOMEM;
                goto err2;
        }
+       helper->data_len = size;
 
        helper->flags |= NF_CT_HELPER_F_USERSPACE;
        memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple));
index e7d0fe3f43304433d315dddfafb9bf88e4396997..c5b3202a14cae757d39dba59e8fe42b16494aa13 100644 (file)
@@ -712,6 +712,10 @@ void qrtr_ns_init(void)
                goto err_sock;
        }
 
+       qrtr_ns.workqueue = alloc_workqueue("qrtr_ns_handler", WQ_UNBOUND, 1);
+       if (!qrtr_ns.workqueue)
+               goto err_sock;
+
        qrtr_ns.sock->sk->sk_data_ready = qrtr_ns_data_ready;
 
        sq.sq_port = QRTR_PORT_CTRL;
@@ -720,17 +724,13 @@ void qrtr_ns_init(void)
        ret = kernel_bind(qrtr_ns.sock, (struct sockaddr *)&sq, sizeof(sq));
        if (ret < 0) {
                pr_err("failed to bind to socket\n");
-               goto err_sock;
+               goto err_wq;
        }
 
        qrtr_ns.bcast_sq.sq_family = AF_QIPCRTR;
        qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
        qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
 
-       qrtr_ns.workqueue = alloc_workqueue("qrtr_ns_handler", WQ_UNBOUND, 1);
-       if (!qrtr_ns.workqueue)
-               goto err_sock;
-
        ret = say_hello(&qrtr_ns.bcast_sq);
        if (ret < 0)
                goto err_wq;
index 1a766393be625a694a85d40d10e2fe990c4fdbb7..20577355235a65769f71555ba357c4a027e5c5bc 100644 (file)
@@ -199,6 +199,9 @@ static int tcf_ct_flow_table_add_action_nat(struct net *net,
        const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple;
        struct nf_conntrack_tuple target;
 
+       if (!(ct->status & IPS_NAT_MASK))
+               return 0;
+
        nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
 
        switch (tuple->src.l3num) {
index a9da8776bf5b59ca1195006fc30c94b877071801..fb760cee824e4cb1807651346d333e2d4590a663 100644 (file)
@@ -297,9 +297,9 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt,
                        goto flow_error;
                }
                q->flows_cnt = nla_get_u32(tb[TCA_FQ_PIE_FLOWS]);
-               if (!q->flows_cnt || q->flows_cnt > 65536) {
+               if (!q->flows_cnt || q->flows_cnt >= 65536) {
                        NL_SET_ERR_MSG_MOD(extack,
-                                          "Number of flows must be < 65536");
+                                          "Number of flows must range in [1..65535]");
                        goto flow_error;
                }
        }
index 6e2eb1dd64ed0fef50c581d6a334b0f599cf6b99..68934438ee19c62e80eda2eb9ab222d6af4e8b80 100644 (file)
@@ -31,7 +31,7 @@ menuconfig IP_SCTP
          homing at either or both ends of an association."
 
          To compile this protocol support as a module, choose M here: the
-         module will be called sctp. Debug messages are handeled by the
+         module will be called sctp. Debug messages are handled by the
          kernel's dynamic debugging framework.
 
          If in doubt, say N.
index 4278764d82b82731483c15023d81ce7b6fab4b9b..83e97e8892e05a5e3bef578626296fa5e14efa71 100644 (file)
@@ -741,14 +741,8 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
        if (crypto_shash_setkey(tfm, &asoc_key->data[0], asoc_key->len))
                goto free;
 
-       {
-               SHASH_DESC_ON_STACK(desc, tfm);
-
-               desc->tfm = tfm;
-               crypto_shash_digest(desc, (u8 *)auth,
-                                   end - (unsigned char *)auth, digest);
-               shash_desc_zero(desc);
-       }
+       crypto_shash_tfm_digest(tfm, (u8 *)auth, end - (unsigned char *)auth,
+                               digest);
 
 free:
        if (free_key)
index f7cb0b7faec2327ea803d5159eea22fe9f612c2f..47910470e53283d9a9143120049c43b0b7db1894 100644 (file)
@@ -1670,17 +1670,14 @@ static struct sctp_cookie_param *sctp_pack_cookie(
               ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len);
 
        if (sctp_sk(ep->base.sk)->hmac) {
-               SHASH_DESC_ON_STACK(desc, sctp_sk(ep->base.sk)->hmac);
+               struct crypto_shash *tfm = sctp_sk(ep->base.sk)->hmac;
                int err;
 
                /* Sign the message.  */
-               desc->tfm = sctp_sk(ep->base.sk)->hmac;
-
-               err = crypto_shash_setkey(desc->tfm, ep->secret_key,
+               err = crypto_shash_setkey(tfm, ep->secret_key,
                                          sizeof(ep->secret_key)) ?:
-                     crypto_shash_digest(desc, (u8 *)&cookie->c, bodysize,
-                                         cookie->signature);
-               shash_desc_zero(desc);
+                     crypto_shash_tfm_digest(tfm, (u8 *)&cookie->c, bodysize,
+                                             cookie->signature);
                if (err)
                        goto free_cookie;
        }
@@ -1741,17 +1738,13 @@ struct sctp_association *sctp_unpack_cookie(
 
        /* Check the signature.  */
        {
-               SHASH_DESC_ON_STACK(desc, sctp_sk(ep->base.sk)->hmac);
+               struct crypto_shash *tfm = sctp_sk(ep->base.sk)->hmac;
                int err;
 
-               desc->tfm = sctp_sk(ep->base.sk)->hmac;
-
-               err = crypto_shash_setkey(desc->tfm, ep->secret_key,
+               err = crypto_shash_setkey(tfm, ep->secret_key,
                                          sizeof(ep->secret_key)) ?:
-                     crypto_shash_digest(desc, (u8 *)bear_cookie, bodysize,
-                                         digest);
-               shash_desc_zero(desc);
-
+                     crypto_shash_tfm_digest(tfm, (u8 *)bear_cookie, bodysize,
+                                             digest);
                if (err) {
                        *error = -SCTP_IERROR_NOMEM;
                        goto fail;
index c82dbdcf13f2ffe13d10a2acac67ed26b1f6c0ea..77d5c36a8991c853bcd05a35bcd8ba48385cb165 100644 (file)
@@ -343,6 +343,9 @@ void sctp_ulpevent_nofity_peer_addr_change(struct sctp_transport *transport,
        struct sockaddr_storage addr;
        struct sctp_ulpevent *event;
 
+       if (asoc->state < SCTP_STATE_ESTABLISHED)
+               return;
+
        memset(&addr, 0, sizeof(struct sockaddr_storage));
        memcpy(&addr, &transport->ipaddr, transport->af_specific->sockaddr_len);
 
index 2d399b6c407564b5da022ab358d92468359c0ece..8c2763eb6aae29589dbdfef3c7cd3f4e01f09c56 100644 (file)
@@ -206,10 +206,12 @@ static void tls_decrypt_done(struct crypto_async_request *req, int err)
 
        kfree(aead_req);
 
+       spin_lock_bh(&ctx->decrypt_compl_lock);
        pending = atomic_dec_return(&ctx->decrypt_pending);
 
-       if (!pending && READ_ONCE(ctx->async_notify))
+       if (!pending && ctx->async_notify)
                complete(&ctx->async_wait.completion);
+       spin_unlock_bh(&ctx->decrypt_compl_lock);
 }
 
 static int tls_do_decryption(struct sock *sk,
@@ -467,10 +469,12 @@ static void tls_encrypt_done(struct crypto_async_request *req, int err)
                        ready = true;
        }
 
+       spin_lock_bh(&ctx->encrypt_compl_lock);
        pending = atomic_dec_return(&ctx->encrypt_pending);
 
-       if (!pending && READ_ONCE(ctx->async_notify))
+       if (!pending && ctx->async_notify)
                complete(&ctx->async_wait.completion);
+       spin_unlock_bh(&ctx->encrypt_compl_lock);
 
        if (!ready)
                return;
@@ -929,6 +933,7 @@ int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        int num_zc = 0;
        int orig_size;
        int ret = 0;
+       int pending;
 
        if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
                return -EOPNOTSUPP;
@@ -1095,13 +1100,19 @@ trim_sgl:
                goto send_end;
        } else if (num_zc) {
                /* Wait for pending encryptions to get completed */
-               smp_store_mb(ctx->async_notify, true);
+               spin_lock_bh(&ctx->encrypt_compl_lock);
+               ctx->async_notify = true;
 
-               if (atomic_read(&ctx->encrypt_pending))
+               pending = atomic_read(&ctx->encrypt_pending);
+               spin_unlock_bh(&ctx->encrypt_compl_lock);
+               if (pending)
                        crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
                else
                        reinit_completion(&ctx->async_wait.completion);
 
+               /* There can be no concurrent accesses, since we have no
+                * pending encrypt operations
+                */
                WRITE_ONCE(ctx->async_notify, false);
 
                if (ctx->async_wait.err) {
@@ -1732,6 +1743,7 @@ int tls_sw_recvmsg(struct sock *sk,
        bool is_kvec = iov_iter_is_kvec(&msg->msg_iter);
        bool is_peek = flags & MSG_PEEK;
        int num_async = 0;
+       int pending;
 
        flags |= nonblock;
 
@@ -1894,8 +1906,11 @@ pick_next_record:
 recv_end:
        if (num_async) {
                /* Wait for all previously submitted records to be decrypted */
-               smp_store_mb(ctx->async_notify, true);
-               if (atomic_read(&ctx->decrypt_pending)) {
+               spin_lock_bh(&ctx->decrypt_compl_lock);
+               ctx->async_notify = true;
+               pending = atomic_read(&ctx->decrypt_pending);
+               spin_unlock_bh(&ctx->decrypt_compl_lock);
+               if (pending) {
                        err = crypto_wait_req(-EINPROGRESS, &ctx->async_wait);
                        if (err) {
                                /* one of async decrypt failed */
@@ -1907,6 +1922,10 @@ recv_end:
                } else {
                        reinit_completion(&ctx->async_wait.completion);
                }
+
+               /* There can be no concurrent accesses, since we have no
+                * pending decrypt operations
+                */
                WRITE_ONCE(ctx->async_notify, false);
 
                /* Drain records from the rx_list & copy if required */
@@ -2293,6 +2312,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
 
        if (tx) {
                crypto_init_wait(&sw_ctx_tx->async_wait);
+               spin_lock_init(&sw_ctx_tx->encrypt_compl_lock);
                crypto_info = &ctx->crypto_send.info;
                cctx = &ctx->tx;
                aead = &sw_ctx_tx->aead_send;
@@ -2301,6 +2321,7 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx)
                sw_ctx_tx->tx_work.sk = sk;
        } else {
                crypto_init_wait(&sw_ctx_rx->async_wait);
+               spin_lock_init(&sw_ctx_rx->decrypt_compl_lock);
                crypto_info = &ctx->crypto_recv.info;
                cctx = &ctx->rx;
                skb_queue_head_init(&sw_ctx_rx->rx_list);
index a5f28708e0e75402e595a38ee91c57e9637e0289..626bf9044418cc78eef4da904334f5dfc87b458c 100644 (file)
@@ -1408,7 +1408,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags,
        /* Wait for children sockets to appear; these are the new sockets
         * created upon connection establishment.
         */
-       timeout = sock_sndtimeo(listener, flags & O_NONBLOCK);
+       timeout = sock_rcvtimeo(listener, flags & O_NONBLOCK);
        prepare_to_wait(sk_sleep(listener), &wait, TASK_INTERRUPTIBLE);
 
        while ((connected = vsock_dequeue_accept(listener)) == NULL &&
index 69efc891885f6ef3c309bdf8a718983b5c299561..0edda1edf9882f702b9073bf82481f01eca630f0 100644 (file)
@@ -1132,6 +1132,14 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
 
        lock_sock(sk);
 
+       /* Check if sk has been released before lock_sock */
+       if (sk->sk_shutdown == SHUTDOWN_MASK) {
+               (void)virtio_transport_reset_no_sock(t, pkt);
+               release_sock(sk);
+               sock_put(sk);
+               goto free_pkt;
+       }
+
        /* Update CID in case it has changed after a transport reset event */
        vsk->local_addr.svm_cid = dst.svm_cid;
 
index 341402b4f178808191d5fe1892747d9d92cbd3d1..ce024440fa51f0d0d87f88d831bcbcd98f62ba2f 100644 (file)
@@ -142,7 +142,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
        if (result)
                return result;
 
-       if (rdev->wiphy.debugfsdir)
+       if (!IS_ERR_OR_NULL(rdev->wiphy.debugfsdir))
                debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
                               rdev->wiphy.debugfsdir,
                               rdev->wiphy.debugfsdir->d_parent, newname);
index ed7a6060f73cadc9c0b812898be9132387a70846..3889bd9aec466d3532509954ad788714537b4477 100644 (file)
@@ -341,8 +341,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
 {
        bool unaligned_chunks = mr->flags & XDP_UMEM_UNALIGNED_CHUNK_FLAG;
        u32 chunk_size = mr->chunk_size, headroom = mr->headroom;
+       u64 npgs, addr = mr->addr, size = mr->len;
        unsigned int chunks, chunks_per_page;
-       u64 addr = mr->addr, size = mr->len;
        int err;
 
        if (chunk_size < XDP_UMEM_MIN_CHUNK_SIZE || chunk_size > PAGE_SIZE) {
@@ -372,6 +372,10 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
        if ((addr + size) < addr)
                return -EINVAL;
 
+       npgs = div_u64(size, PAGE_SIZE);
+       if (npgs > U32_MAX)
+               return -EINVAL;
+
        chunks = (unsigned int)div_u64(size, chunk_size);
        if (chunks == 0)
                return -EINVAL;
@@ -391,7 +395,7 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
        umem->size = size;
        umem->headroom = headroom;
        umem->chunk_size_nohr = chunk_size - headroom;
-       umem->npgs = size / PAGE_SIZE;
+       umem->npgs = (u32)npgs;
        umem->pgs = NULL;
        umem->user = NULL;
        umem->flags = mr->flags;
index 037ea156d2f93b5e0f5611f884f766cacfaefff9..5a0ff665b71a8dd4125827b13fcd8c3263e74142 100644 (file)
@@ -379,6 +379,7 @@ static void espintcp_destruct(struct sock *sk)
 {
        struct espintcp_ctx *ctx = espintcp_getctx(sk);
 
+       ctx->saved_destruct(sk);
        kfree(ctx);
 }
 
@@ -419,6 +420,7 @@ static int espintcp_init_sk(struct sock *sk)
        sk->sk_socket->ops = &espintcp_ops;
        ctx->saved_data_ready = sk->sk_data_ready;
        ctx->saved_write_space = sk->sk_write_space;
+       ctx->saved_destruct = sk->sk_destruct;
        sk->sk_data_ready = espintcp_data_ready;
        sk->sk_write_space = espintcp_write_space;
        sk->sk_destruct = espintcp_destruct;
index 6cc7f7f1dd68cdad76734bb05e11dd90d714c86c..f50d1f97cf8ec9e1c1464321e07e3fabfb1fbce6 100644 (file)
@@ -25,12 +25,10 @@ static void __xfrm_transport_prep(struct xfrm_state *x, struct sk_buff *skb,
        struct xfrm_offload *xo = xfrm_offload(skb);
 
        skb_reset_mac_len(skb);
-       pskb_pull(skb, skb->mac_len + hsize + x->props.header_len);
-
-       if (xo->flags & XFRM_GSO_SEGMENT) {
-               skb_reset_transport_header(skb);
+       if (xo->flags & XFRM_GSO_SEGMENT)
                skb->transport_header -= x->props.header_len;
-       }
+
+       pskb_pull(skb, skb_transport_offset(skb) + x->props.header_len);
 }
 
 static void __xfrm_mode_tunnel_prep(struct xfrm_state *x, struct sk_buff *skb,
index aa35f23c491292f362858660e14d9a9e786c7445..8a202c44f89aefeed1a2036350b2dfc06d0962cc 100644 (file)
@@ -644,7 +644,7 @@ resume:
                dev_put(skb->dev);
 
                spin_lock(&x->lock);
-               if (nexthdr <= 0) {
+               if (nexthdr < 0) {
                        if (nexthdr == -EBADMSG) {
                                xfrm_audit_state_icvfail(x, skb,
                                                         x->type->proto);
index 3361e3ac5714cc6c751afe3eed996f8956180696..1e115cbf21d3b18bbda35754b9f544fa76a5daa4 100644 (file)
@@ -750,7 +750,28 @@ static struct rtnl_link_ops xfrmi_link_ops __read_mostly = {
        .get_link_net   = xfrmi_get_link_net,
 };
 
+static void __net_exit xfrmi_exit_batch_net(struct list_head *net_exit_list)
+{
+       struct net *net;
+       LIST_HEAD(list);
+
+       rtnl_lock();
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
+               struct xfrm_if __rcu **xip;
+               struct xfrm_if *xi;
+
+               for (xip = &xfrmn->xfrmi[0];
+                    (xi = rtnl_dereference(*xip)) != NULL;
+                    xip = &xi->next)
+                       unregister_netdevice_queue(xi->dev, &list);
+       }
+       unregister_netdevice_many(&list);
+       rtnl_unlock();
+}
+
 static struct pernet_operations xfrmi_net_ops = {
+       .exit_batch = xfrmi_exit_batch_net,
        .id   = &xfrmi_net_id,
        .size = sizeof(struct xfrmi_net),
 };
index 2fd3d990d992fe26ddfa3ea870e0940ce58dec0d..69c4900db8172a486f53859665c7ebe373d313f1 100644 (file)
@@ -583,18 +583,20 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
                xfrm_state_hold(x);
 
                if (skb_is_gso(skb)) {
-                       skb_shinfo(skb)->gso_type |= SKB_GSO_ESP;
+                       if (skb->inner_protocol)
+                               return xfrm_output_gso(net, sk, skb);
 
-                       return xfrm_output2(net, sk, skb);
+                       skb_shinfo(skb)->gso_type |= SKB_GSO_ESP;
+                       goto out;
                }
 
                if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)
                        goto out;
+       } else {
+               if (skb_is_gso(skb))
+                       return xfrm_output_gso(net, sk, skb);
        }
 
-       if (skb_is_gso(skb))
-               return xfrm_output_gso(net, sk, skb);
-
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                err = skb_checksum_help(skb);
                if (err) {
@@ -640,7 +642,8 @@ void xfrm_local_error(struct sk_buff *skb, int mtu)
 
        if (skb->protocol == htons(ETH_P_IP))
                proto = AF_INET;
-       else if (skb->protocol == htons(ETH_P_IPV6))
+       else if (skb->protocol == htons(ETH_P_IPV6) &&
+                skb->sk->sk_family == AF_INET6)
                proto = AF_INET6;
        else
                return;
index 297b2fdb3c2977fd57c3a640550d50b5b661902c..564aa6492e7c397312f51e350a84b2063c5ed616 100644 (file)
@@ -1436,12 +1436,7 @@ static void xfrm_policy_requeue(struct xfrm_policy *old,
 static bool xfrm_policy_mark_match(struct xfrm_policy *policy,
                                   struct xfrm_policy *pol)
 {
-       u32 mark = policy->mark.v & policy->mark.m;
-
-       if (policy->mark.v == pol->mark.v && policy->mark.m == pol->mark.m)
-               return true;
-
-       if ((mark & pol->mark.m) == pol->mark.v &&
+       if (policy->mark.v == pol->mark.v &&
            policy->priority == pol->priority)
                return true;
 
index 9d236c346de506dfcf65cba58dbec3507659bc18..205076cf234ee12a91fc86ffe9b12f718049fb81 100644 (file)
@@ -171,7 +171,7 @@ config SAMPLE_VFIO_MDEV_MBOCHS
 
 config SAMPLE_ANDROID_BINDERFS
        bool "Build Android binderfs example"
-       depends on CONFIG_ANDROID_BINDERFS
+       depends on ANDROID_BINDERFS
        help
          Builds a sample program to illustrate the use of the Android binderfs
          filesystem.
index ea4c93d362569e27b1ba194965bf39d892f42af4..a3ac5476338a47bbf54a467b0498a32b19574692 100644 (file)
@@ -1,2 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs_example.o
+ifndef CROSS_COMPILE
+ifdef CONFIG_SAMPLE_ANDROID_BINDERFS
+hostprogs := binderfs_example
+endif
+endif
index e04229d214753267e6f5eb3978a921ef278d4af6..c13a5bc5095bea5cb1dbb1fba19ef3a49ce4cf08 100644 (file)
@@ -20,18 +20,22 @@ static unsigned long my_ip = (unsigned long)schedule;
 
 asm (
 "      .pushsection    .text, \"ax\", @progbits\n"
+"      .type           my_tramp1, @function\n"
 "   my_tramp1:"
 "      pushq %rbp\n"
 "      movq %rsp, %rbp\n"
 "      call my_direct_func1\n"
 "      leave\n"
+"      .size           my_tramp1, .-my_tramp1\n"
 "      ret\n"
+"      .type           my_tramp2, @function\n"
 "   my_tramp2:"
 "      pushq %rbp\n"
 "      movq %rsp, %rbp\n"
 "      call my_direct_func2\n"
 "      leave\n"
 "      ret\n"
+"      .size           my_tramp2, .-my_tramp2\n"
 "      .popsection\n"
 );
 
index 27efa5f6ff5285cf06472a02ef60b7d1e0e71c4a..d5c5022be66429be712c530eafdea7ce86299244 100644 (file)
@@ -15,6 +15,7 @@ extern void my_tramp(void *);
 
 asm (
 "      .pushsection    .text, \"ax\", @progbits\n"
+"      .type           my_tramp, @function\n"
 "   my_tramp:"
 "      pushq %rbp\n"
 "      movq %rsp, %rbp\n"
@@ -27,6 +28,7 @@ asm (
 "      popq %rdi\n"
 "      leave\n"
 "      ret\n"
+"      .size           my_tramp, .-my_tramp\n"
 "      .popsection\n"
 );
 
index a2e3063bd306b829d0d7ef082e7fc2d6cb4d619e..63ca06d42c803ff8ad846d446f91be45bd71d000 100644 (file)
@@ -13,6 +13,7 @@ extern void my_tramp(void *);
 
 asm (
 "      .pushsection    .text, \"ax\", @progbits\n"
+"      .type           my_tramp, @function\n"
 "   my_tramp:"
 "      pushq %rbp\n"
 "      movq %rsp, %rbp\n"
@@ -21,6 +22,7 @@ asm (
 "      popq %rdi\n"
 "      leave\n"
 "      ret\n"
+"      .size           my_tramp, .-my_tramp\n"
 "      .popsection\n"
 );
 
index d693c23a85e8a0f21df776d48463950ab13cedaa..501911d1b32789ccf2275941bbcb98c1ff790267 100644 (file)
@@ -25,7 +25,7 @@ static struct kprobe kp = {
 };
 
 /* kprobe pre_handler: called just before the probed instruction is executed */
-static int handler_pre(struct kprobe *p, struct pt_regs *regs)
+static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
 {
 #ifdef CONFIG_X86
        pr_info("<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx\n",
@@ -54,7 +54,7 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs)
 }
 
 /* kprobe post_handler: called after the probed instruction is executed */
-static void handler_post(struct kprobe *p, struct pt_regs *regs,
+static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
                                unsigned long flags)
 {
 #ifdef CONFIG_X86
@@ -90,6 +90,8 @@ static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
        /* Return 0 because we don't handle the fault. */
        return 0;
 }
+/* NOKPROBE_SYMBOL() is also available */
+NOKPROBE_SYMBOL(handler_fault);
 
 static int __init kprobe_init(void)
 {
index 186315ca88b3f7c01d45bf463de15bbcd935a905..013e8e6ebae9a56010b99230bfe6b626960519dd 100644 (file)
@@ -48,6 +48,7 @@ static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
        data->entry_stamp = ktime_get();
        return 0;
 }
+NOKPROBE_SYMBOL(entry_handler);
 
 /*
  * Return-probe handler: Log the return value and duration. Duration may turn
@@ -67,6 +68,7 @@ static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
                        func_name, retval, (long long)delta);
        return 0;
 }
+NOKPROBE_SYMBOL(ret_handler);
 
 static struct kretprobe my_kretprobe = {
        .handler                = ret_handler,
index a3d68159fb5105482fdda5819fdf7ee7d0e8ad3f..76c577ea4fd8b602878aab8b961b20bc2f3fe607 100644 (file)
@@ -216,7 +216,7 @@ int main(int argc, char **argv)
        struct statx stx;
        int ret, raw = 0, atflag = AT_SYMLINK_NOFOLLOW;
 
-       unsigned int mask = STATX_ALL;
+       unsigned int mask = STATX_BASIC_STATS | STATX_BTIME;
 
        for (argv++; *argv; argv++) {
                if (strcmp(*argv, "-F") == 0) {
index eac40f0abd56a9f4700423664824da8a537bfd61..b83be177edf0ac70760171665700847257bfbe5b 100755 (executable)
@@ -51,7 +51,7 @@ my %ignore_type = ();
 my @ignore = ();
 my $help = 0;
 my $configuration_file = ".checkpatch.conf";
-my $max_line_length = 80;
+my $max_line_length = 100;
 my $ignore_perl_version = 0;
 my $minimum_perl_version = 5.10.0;
 my $min_conf_desc_length = 4;
@@ -97,9 +97,11 @@ Options:
   --types TYPE(,TYPE2...)    show only these comma separated message types
   --ignore TYPE(,TYPE2...)   ignore various comma separated message types
   --show-types               show the specific message type in the output
-  --max-line-length=n        set the maximum line length, if exceeded, warn
+  --max-line-length=n        set the maximum line length, (default $max_line_length)
+                             if exceeded, warn on patches
+                             requires --strict for use with --file
   --min-conf-desc-length=n   set the min description length, if shorter, warn
-  --tab-size=n               set the number of spaces for tab (default 8)
+  --tab-size=n               set the number of spaces for tab (default $tabsize)
   --root=PATH                PATH to the kernel tree root
   --no-summary               suppress the per-file summary
   --mailback                 only produce a report in case of warnings/errors
@@ -3240,8 +3242,10 @@ sub process {
 
                        if ($msg_type ne "" &&
                            (show_type("LONG_LINE") || show_type($msg_type))) {
-                               WARN($msg_type,
-                                    "line over $max_line_length characters\n" . $herecurr);
+                               my $msg_level = \&WARN;
+                               $msg_level = \&CHK if ($file);
+                               &{$msg_level}($msg_type,
+                                             "line length of $length exceeds $max_line_length columns\n" . $herecurr);
                        }
                }
 
index f746ca8fa403c43fc63cfbbe6d637bd06d61ff08..f68d76dd97badf39f403726f384026643063c28b 100755 (executable)
@@ -213,7 +213,9 @@ my $type_constant = '\b``([^\`]+)``\b';
 my $type_constant2 = '\%([-_\w]+)';
 my $type_func = '(\w+)\(\)';
 my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
+my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
 my $type_fp_param = '\@(\w+)\(\)';  # Special RST handling for func ptr params
+my $type_fp_param2 = '\@(\w+->\S+)\(\)';  # Special RST handling for structs with func ptr params
 my $type_env = '(\$\w+)';
 my $type_enum = '\&(enum\s*([_\w]+))';
 my $type_struct = '\&(struct\s*([_\w]+))';
@@ -236,6 +238,7 @@ my @highlights_man = (
                       [$type_typedef, "\\\\fI\$1\\\\fP"],
                       [$type_union, "\\\\fI\$1\\\\fP"],
                       [$type_param, "\\\\fI\$1\\\\fP"],
+                      [$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
                       [$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
                       [$type_fallback, "\\\\fI\$1\\\\fP"]
                     );
@@ -249,6 +252,7 @@ my @highlights_rst = (
                        [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
                        [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
                       [$type_fp_param, "**\$1\\\\(\\\\)**"],
+                      [$type_fp_param2, "**\$1\\\\(\\\\)**"],
                        [$type_func, "\$1()"],
                        [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
                        [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
@@ -256,7 +260,7 @@ my @highlights_rst = (
                        [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
                        # in rst this can refer to any type
                        [$type_fallback, "\\:c\\:type\\:`\$1`"],
-                       [$type_param, "**\$1**"]
+                       [$type_param_ref, "**\$1\$2**"]
                      );
 my $blankline_rst = "\n";
 
@@ -327,13 +331,14 @@ my $lineprefix="";
 
 # Parser states
 use constant {
-    STATE_NORMAL        => 0, # normal code
-    STATE_NAME          => 1, # looking for function name
-    STATE_BODY_MAYBE    => 2, # body - or maybe more description
-    STATE_BODY          => 3, # the body of the comment
-    STATE_PROTO         => 4, # scanning prototype
-    STATE_DOCBLOCK      => 5, # documentation block
-    STATE_INLINE        => 6, # gathering documentation outside main block
+    STATE_NORMAL        => 0,        # normal code
+    STATE_NAME          => 1,        # looking for function name
+    STATE_BODY_MAYBE    => 2,        # body - or maybe more description
+    STATE_BODY          => 3,        # the body of the comment
+    STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
+    STATE_PROTO         => 5,        # scanning prototype
+    STATE_DOCBLOCK      => 6,        # documentation block
+    STATE_INLINE        => 7,        # gathering doc outside main block
 };
 my $state;
 my $in_doc_sect;
@@ -1953,6 +1958,12 @@ sub process_body($$) {
        }
     }
 
+    if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) {
+       dump_section($file, $section, $contents);
+       $section = $section_default;
+       $contents = "";
+    }
+
     if (/$doc_sect/i) { # case insensitive for supported section names
        $newsection = $1;
        $newcontents = $2;
@@ -2006,18 +2017,21 @@ sub process_body($$) {
        $state = STATE_PROTO;
        $brcount = 0;
     } elsif (/$doc_content/) {
-       # miguel-style comment kludge, look for blank lines after
-       # @parameter line to signify start of description
        if ($1 eq "") {
-           if ($section =~ m/^@/ || $section eq $section_context) {
+           if ($section eq $section_context) {
                dump_section($file, $section, $contents);
                $section = $section_default;
                $contents = "";
                $new_start_line = $.;
+               $state = STATE_BODY;
            } else {
+               if ($section ne $section_default) {
+                   $state = STATE_BODY_WITH_BLANK_LINE;
+               } else {
+                   $state = STATE_BODY;
+               }
                $contents .= "\n";
            }
-           $state = STATE_BODY;
        } elsif ($state == STATE_BODY_MAYBE) {
            # Continued declaration purpose
            chomp($declaration_purpose);
@@ -2169,7 +2183,8 @@ sub process_file($) {
            process_normal();
        } elsif ($state == STATE_NAME) {
            process_name($file, $_);
-       } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE) {
+       } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE ||
+                $state == STATE_BODY_WITH_BLANK_LINE) {
            process_body($file, $_);
        } elsif ($state == STATE_INLINE) { # scanning for inline parameters
            process_inline($file, $_);
index d09ab4afbda4931a4aef8d34eb2f64bf3d8e9fd9..3adef49250af979b22e3180ae05a8adb382a29b6 100755 (executable)
@@ -55,6 +55,29 @@ modpost_link()
        ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
 }
 
+objtool_link()
+{
+       local objtoolopt;
+
+       if [ -n "${CONFIG_VMLINUX_VALIDATION}" ]; then
+               objtoolopt="check"
+               if [ -z "${CONFIG_FRAME_POINTER}" ]; then
+                       objtoolopt="${objtoolopt} --no-fp"
+               fi
+               if [ -n "${CONFIG_GCOV_KERNEL}" ]; then
+                       objtoolopt="${objtoolopt} --no-unreachable"
+               fi
+               if [ -n "${CONFIG_RETPOLINE}" ]; then
+                       objtoolopt="${objtoolopt} --retpoline"
+               fi
+               if [ -n "${CONFIG_X86_SMAP}" ]; then
+                       objtoolopt="${objtoolopt} --uaccess"
+               fi
+               info OBJTOOL ${1}
+               tools/objtool/objtool ${objtoolopt} ${1}
+       fi
+}
+
 # Link of vmlinux
 # ${1} - output file
 # ${2}, ${3}, ... - optional extra .o files
@@ -251,6 +274,7 @@ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init need-builtin=1
 #link vmlinux.o
 info LD vmlinux.o
 modpost_link vmlinux.o
+objtool_link vmlinux.o
 
 # modpost vmlinux.o to check for section mismatches
 ${MAKE} -f "${srctree}/scripts/Makefile.modpost" MODPOST_VMLINUX=1
index 5c3c50c5ec524aaf0e5cbadca2b0d7e8e285b59b..0053d4fea847d6a114e1c2c35daf14e6e818b23b 100644 (file)
@@ -948,7 +948,7 @@ static void check_section(const char *modname, struct elf_info *elf,
 
 #define DATA_SECTIONS ".data", ".data.rel"
 #define TEXT_SECTIONS ".text", ".text.unlikely", ".sched.text", \
-               ".kprobes.text", ".cpuidle.text"
+               ".kprobes.text", ".cpuidle.text", ".noinstr.text"
 #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
                ".fixup", ".entry.text", ".exception.text", ".text.*", \
                ".coldtext"
index fa3fb05cd54bea8f67f906f150fafcc508a2a769..c680c3efb176ef7764f1538c6036ce436c8d8125 100755 (executable)
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 use strict;
 
-# Copyright (c) 2017-2019 Mauro Carvalho Chehab <mchehab@kernel.org>
+# Copyright (c) 2017-2020 Mauro Carvalho Chehab <mchehab@kernel.org>
 #
 
 my $prefix = "./";
@@ -22,10 +22,16 @@ my $need = 0;
 my $optional = 0;
 my $need_symlink = 0;
 my $need_sphinx = 0;
+my $need_venv = 0;
+my $need_virtualenv = 0;
 my $rec_sphinx_upgrade = 0;
 my $install = "";
 my $virtenv_dir = "";
+my $python_cmd = "";
 my $min_version;
+my $cur_version;
+my $rec_version = "1.7.9";     # PDF won't build here
+my $min_pdf_version = "2.4.4"; # Min version where pdf builds
 
 #
 # Command line arguments
@@ -142,12 +148,30 @@ sub findprog($)
        }
 }
 
+sub find_python_no_venv()
+{
+       my $prog = shift;
+
+       my $cur_dir = qx(pwd);
+       $cur_dir =~ s/\s+$//;
+
+       foreach my $dir (split(/:/, $ENV{PATH})) {
+               next if ($dir =~ m,($cur_dir)/sphinx,);
+               return "$dir/python3" if(-x "$dir/python3");
+       }
+       foreach my $dir (split(/:/, $ENV{PATH})) {
+               next if ($dir =~ m,($cur_dir)/sphinx,);
+               return "$dir/python" if(-x "$dir/python");
+       }
+       return "python";
+}
+
 sub check_program($$)
 {
        my $prog = shift;
        my $is_optional = shift;
 
-       return if findprog($prog);
+       return $prog if findprog($prog);
 
        add_package($prog, $is_optional);
 }
@@ -168,9 +192,9 @@ sub check_python_module($$)
        my $prog = shift;
        my $is_optional = shift;
 
-       my $err = system("python3 -c 'import $prog' 2>/dev/null /dev/null");
-       return if ($err == 0);
-       my $err = system("python -c 'import $prog' 2>/dev/null /dev/null");
+       return if (!$python_cmd);
+
+       my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null");
        return if ($err == 0);
 
        add_package($prog, $is_optional);
@@ -225,23 +249,33 @@ sub get_sphinx_fname()
                return $fname;
        }
 
-       if ($virtualenv) {
-               my $prog = findprog("virtualenv-3");
-               $prog = findprog("virtualenv-3.5") if (!$prog);
+       return "";
+}
 
-               check_program("virtualenv", 0) if (!$prog);
-               $need_sphinx = 1;
-       } else {
-               add_package("python-sphinx", 0);
-       }
+sub get_sphinx_version($)
+{
+       my $cmd = shift;
+       my $ver;
 
-       return "";
+       open IN, "$cmd --version 2>&1 |";
+       while (<IN>) {
+               if (m/^\s*sphinx-build\s+([\d\.]+)(\+\/[\da-f]+)?$/) {
+                       $ver=$1;
+                       last;
+               }
+               # Sphinx 1.2.x uses a different format
+               if (m/^\s*Sphinx.*\s+([\d\.]+)$/) {
+                       $ver=$1;
+                       last;
+               }
+       }
+       close IN;
+       return $ver;
 }
 
 sub check_sphinx()
 {
-       my $rec_version;
-       my $cur_version;
+       my $default_version;
 
        open IN, $conf or die "Can't open $conf";
        while (<IN>) {
@@ -257,45 +291,39 @@ sub check_sphinx()
        open IN, $requirement_file or die "Can't open $requirement_file";
        while (<IN>) {
                if (m/^\s*Sphinx\s*==\s*([\d\.]+)$/) {
-                       $rec_version=$1;
+                       $default_version=$1;
                        last;
                }
        }
        close IN;
 
-       die "Can't get recommended sphinx version from $requirement_file" if (!$min_version);
+       die "Can't get default sphinx version from $requirement_file" if (!$default_version);
 
-       $virtenv_dir = $virtenv_prefix . $rec_version;
+       $virtenv_dir = $virtenv_prefix . $default_version;
 
        my $sphinx = get_sphinx_fname();
-       return if ($sphinx eq "");
-
-       open IN, "$sphinx --version 2>&1 |" or die "$sphinx returned an error";
-       while (<IN>) {
-               if (m/^\s*sphinx-build\s+([\d\.]+)(\+\/[\da-f]+)?$/) {
-                       $cur_version=$1;
-                       last;
-               }
-               # Sphinx 1.2.x uses a different format
-               if (m/^\s*Sphinx.*\s+([\d\.]+)$/) {
-                       $cur_version=$1;
-                       last;
-               }
+       if ($sphinx eq "") {
+               $need_sphinx = 1;
+               return;
        }
-       close IN;
+
+       $cur_version = get_sphinx_version($sphinx);
+       die ("$sphinx returned an error") if (!$cur_version);
 
        die "$sphinx didn't return its version" if (!$cur_version);
 
        if ($cur_version lt $min_version) {
                printf "ERROR: Sphinx version is %s. It should be >= %s (recommended >= %s)\n",
-                      $cur_version, $min_version, $rec_version;;
+                      $cur_version, $min_version, $default_version;
                $need_sphinx = 1;
                return;
        }
 
        if ($cur_version lt $rec_version) {
-               printf "Sphinx version %s\n", $cur_version;
-               print "Warning: It is recommended at least Sphinx version $rec_version.\n";
+               $rec_sphinx_upgrade = 1;
+               return;
+       }
+       if ($cur_version lt $min_pdf_version) {
                $rec_sphinx_upgrade = 1;
                return;
        }
@@ -336,6 +364,7 @@ sub give_debian_hints()
        my %map = (
                "python-sphinx"         => "python3-sphinx",
                "sphinx_rtd_theme"      => "python3-sphinx-rtd-theme",
+               "ensurepip"             => "python3-venv",
                "virtualenv"            => "virtualenv",
                "dot"                   => "graphviz",
                "convert"               => "imagemagick",
@@ -349,7 +378,8 @@ sub give_debian_hints()
                                   "fonts-dejavu", 2);
 
                check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
-                                  "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"],
+                                   "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
+                                   "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"],
                                   "fonts-noto-cjk", 2);
        }
 
@@ -446,9 +476,11 @@ sub give_opensuse_hints()
                "convert"               => "ImageMagick",
                "Pod::Usage"            => "perl-Pod-Usage",
                "xelatex"               => "texlive-xetex-bin",
-               "rsvg-convert"          => "rsvg-view",
        );
 
+       # On Tumbleweed, this package is also named rsvg-convert
+       $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/));
+
        my @suse_tex_pkgs = (
                "texlive-babel-english",
                "texlive-caption",
@@ -491,7 +523,7 @@ sub give_mageia_hints()
                "convert"               => "ImageMagick",
                "Pod::Usage"            => "perl-Pod-Usage",
                "xelatex"               => "texlive",
-               "rsvg-convert"          => "librsvg2-tools",
+               "rsvg-convert"          => "librsvg2",
        );
 
        my @tex_pkgs = (
@@ -500,16 +532,29 @@ sub give_mageia_hints()
 
        $map{"latexmk"} = "texlive-collection-basic";
 
+       my $packager_cmd;
+       my $noto_sans;
+       if ($system_release =~ /OpenMandriva/) {
+               $packager_cmd = "dnf install";
+               $noto_sans = "noto-sans-cjk-fonts";
+               @tex_pkgs = ( "texlive-collection-fontsextra" );
+       } else {
+               $packager_cmd = "urpmi";
+               $noto_sans = "google-noto-sans-cjk-ttc-fonts";
+       }
+
+
        if ($pdf) {
-               check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc"],
-                                  "google-noto-sans-cjk-ttc-fonts", 2);
+               check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
+                                   "/usr/share/fonts/TTF/NotoSans-Regular.ttf"],
+                                  $noto_sans, 2);
        }
 
        check_rpm_missing(\@tex_pkgs, 2) if ($pdf);
        check_missing(\%map);
 
        return if (!$need && !$optional);
-       printf("You should run:\n\n\tsudo urpmi $install\n");
+       printf("You should run:\n\n\tsudo $packager_cmd $install\n");
 }
 
 sub give_arch_linux_hints()
@@ -557,7 +602,8 @@ sub give_gentoo_hints()
                           "media-fonts/dejavu", 2) if ($pdf);
 
        if ($pdf) {
-               check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf"],
+               check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf",
+                                   "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"],
                                   "media-fonts/noto-cjk", 2);
        }
 
@@ -572,10 +618,10 @@ sub give_gentoo_hints()
        my $portage_imagemagick = "/etc/portage/package.use/imagemagick";
        my $portage_cairo = "/etc/portage/package.use/graphviz";
 
-       if (qx(cat $portage_imagemagick) ne "$imagemagick\n") {
+       if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") {
                printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n")
        }
-       if (qx(cat $portage_cairo) ne  "$cairo\n") {
+       if (qx(grep graphviz $portage_cairo 2>/dev/null) eq  "") {
                printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n");
        }
 
@@ -622,6 +668,10 @@ sub check_distros()
                give_mageia_hints;
                return;
        }
+       if ($system_release =~ /OpenMandriva/) {
+               give_mageia_hints;
+               return;
+       }
        if ($system_release =~ /Arch Linux/) {
                give_arch_linux_hints;
                return;
@@ -651,22 +701,58 @@ sub check_distros()
 
 sub deactivate_help()
 {
-       printf "\tIf you want to exit the virtualenv, you can use:\n";
+       printf "\nIf you want to exit the virtualenv, you can use:\n";
        printf "\tdeactivate\n";
 }
 
 sub check_needs()
 {
-       # Check for needed programs/tools
+       # Check if Sphinx is already accessible from current environment
        check_sphinx();
 
        if ($system_release) {
-               print "Detected OS: $system_release.\n\n";
+               print "Detected OS: $system_release.\n";
        } else {
-               print "Unknown OS\n\n";
+               print "Unknown OS\n";
+       }
+       printf "Sphinx version: %s\n\n", $cur_version if ($cur_version);
+
+       # Check python command line, trying first python3
+       $python_cmd = findprog("python3");
+       $python_cmd = check_program("python", 0) if (!$python_cmd);
+
+       # Check the type of virtual env, depending on Python version
+       if ($python_cmd) {
+               if ($virtualenv) {
+                       my $tmp = qx($python_cmd --version 2>&1);
+                       if ($tmp =~ m/(\d+\.)(\d+\.)/) {
+                               if ($1 >= 3 && $2 >= 3) {
+                                       $need_venv = 1;         # python 3.3 or upper
+                               } else {
+                                       $need_virtualenv = 1;
+                               }
+                               if ($1 < 3) {
+                                       # Complain if it finds python2 (or worse)
+                                       printf "Warning: python$1 support is deprecated. Use it with caution!\n";
+                               }
+                       } else {
+                               die "Warning: couldn't identify $python_cmd version!";
+                       }
+               } else {
+                       add_package("python-sphinx", 0);
+               }
        }
 
-       print "To upgrade Sphinx, use:\n\n" if ($rec_sphinx_upgrade);
+       # Set virtualenv command line, if python < 3.3
+       my $virtualenv_cmd;
+       if ($need_virtualenv) {
+               $virtualenv_cmd = findprog("virtualenv-3");
+               $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd);
+               if (!$virtualenv_cmd) {
+                       check_program("virtualenv", 0);
+                       $virtualenv_cmd = "virtualenv";
+               }
+       }
 
        # Check for needed programs/tools
        check_perl_module("Pod::Usage", 0);
@@ -681,46 +767,81 @@ sub check_needs()
        check_program("rsvg-convert", 2) if ($pdf);
        check_program("latexmk", 2) if ($pdf);
 
+       if ($need_sphinx || $rec_sphinx_upgrade) {
+               check_python_module("ensurepip", 0) if ($need_venv);
+       }
+
+       # Do distro-specific checks and output distro-install commands
        check_distros();
 
+       if (!$python_cmd) {
+               if ($need == 1) {
+                       die "Can't build as $need mandatory dependency is missing";
+               } elsif ($need) {
+                       die "Can't build as $need mandatory dependencies are missing";
+               }
+       }
+
+       # Check if sphinx-build is called sphinx-build-3
        if ($need_symlink) {
                printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n",
                       which("sphinx-build-3");
        }
+
+       # NOTE: if the system has a too old Sphinx version installed,
+       # it will recommend installing a newer version using virtualenv
+
        if ($need_sphinx || $rec_sphinx_upgrade) {
                my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate";
                my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate";
 
+               if ($cur_version lt $rec_version) {
+                       print "Warning: It is recommended at least Sphinx version $rec_version.\n";
+                       print "         If you want pdf, you need at least $min_pdf_version.\n";
+               }
+               if ($cur_version lt $min_pdf_version) {
+                       print "Note: It is recommended at least Sphinx version $min_pdf_version if you need PDF support.\n";
+               }
                @activates = sort {$b cmp $a} @activates;
-
-               if ($need_sphinx && scalar @activates > 0 && $activates[0] ge $min_activate) {
-                       printf "\nNeed to activate a compatible Sphinx version on virtualenv with:\n";
-                       printf "\t. $activates[0]\n";
-                       deactivate_help();
-                       exit (1);
-               } else {
-                       my $rec_activate = "$virtenv_dir/bin/activate";
-                       my $virtualenv = findprog("virtualenv-3");
-                       my $rec_python3 = "";
-                       $virtualenv = findprog("virtualenv-3.5") if (!$virtualenv);
-                       $virtualenv = findprog("virtualenv") if (!$virtualenv);
-                       $virtualenv = "virtualenv" if (!$virtualenv);
-
-                       my $rel = "";
-                       if (index($system_release, "Ubuntu") != -1) {
-                               $rel = $1 if ($system_release =~ /Ubuntu\s+(\d+)[.]/);
-                               if ($rel && $rel >= 16) {
-                                       $rec_python3 = " -p python3";
-                               }
+               my ($activate, $ver);
+               foreach my $f (@activates) {
+                       next if ($f lt $min_activate);
+
+                       my $sphinx_cmd = $f;
+                       $sphinx_cmd =~ s/activate/sphinx-build/;
+                       next if (! -f $sphinx_cmd);
+
+                       $ver = get_sphinx_version($sphinx_cmd);
+                       if ($need_sphinx && ($ver ge $min_version)) {
+                               $activate = $f;
+                               last;
+                       } elsif ($ver gt $cur_version) {
+                               $activate = $f;
+                               last;
                        }
-                       if (index($system_release, "Debian") != -1) {
-                               $rel = $1 if ($system_release =~ /Debian\s+(\d+)/);
-                               if ($rel && $rel >= 7) {
-                                       $rec_python3 = " -p python3";
-                               }
+               }
+               if ($activate ne "") {
+                       if ($need_sphinx) {
+                               printf "\nNeed to activate Sphinx (version $ver) on virtualenv with:\n";
+                               printf "\t. $activate\n";
+                               deactivate_help();
+                               exit (1);
+                       } else {
+                               printf "\nYou may also use a newer Sphinx (version $ver) with:\n";
+                               printf "\tdeactivate && . $activate\n";
                        }
+               } else {
+                       my $rec_activate = "$virtenv_dir/bin/activate";
+
+                       print "To upgrade Sphinx, use:\n\n" if ($rec_sphinx_upgrade);
+
+                       $python_cmd = find_python_no_venv();
 
-                       printf "\t$virtualenv$rec_python3 $virtenv_dir\n";
+                       if ($need_venv) {
+                               printf "\t$python_cmd -m venv $virtenv_dir\n";
+                       } else {
+                               printf "\t$virtualenv_cmd $virtenv_dir\n";
+                       }
                        printf "\t. $rec_activate\n";
                        printf "\tpip install -r $requirement_file\n";
                        deactivate_help();
@@ -780,6 +901,24 @@ $system_release = catcheck("/etc/system-release") if !$system_release;
 $system_release = catcheck("/etc/redhat-release") if !$system_release;
 $system_release = catcheck("/etc/lsb-release") if !$system_release;
 $system_release = catcheck("/etc/gentoo-release") if !$system_release;
+
+# This seems more common than LSB these days
+if (!$system_release) {
+       my %os_var;
+       if (open IN, "cat /etc/os-release|") {
+               while (<IN>) {
+                       if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) {
+                               $os_var{$1}=$2;
+                       }
+               }
+               $system_release = $os_var{"NAME"};
+               if (defined($os_var{"VERSION_ID"})) {
+                       $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"}));
+               } else {
+                       $system_release .= " " . $os_var{"VERSION"};
+               }
+       }
+}
 $system_release = catcheck("/etc/issue") if !$system_release;
 $system_release =~ s/\s+$//;
 
index 22e73a3482bd6c393a8a3f712bebf263692d16ca..3baf435de5411b2d2f5965a75faf327b4b79355b 100644 (file)
@@ -30,7 +30,7 @@ obj-$(CONFIG_SECURITY_YAMA)           += yama/
 obj-$(CONFIG_SECURITY_LOADPIN)         += loadpin/
 obj-$(CONFIG_SECURITY_SAFESETID)       += safesetid/
 obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)    += lockdown/
-obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
+obj-$(CONFIG_CGROUPS)                  += device_cgroup.o
 obj-$(CONFIG_BPF_LSM)                  += bpf/
 
 # Object integrity file lists
index f4ee0ae106b282a12adb338f8caa6ce219cfbceb..0ca31c8bc0b13b8dcb70ceabbc466962df162748 100644 (file)
@@ -812,6 +812,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
        int ret;
        kuid_t root_uid;
 
+       new->cap_ambient = old->cap_ambient;
        if (WARN_ON(!cap_ambient_invariant_ok(old)))
                return -EPERM;
 
index 7d0f8f7431ff5466af18e6105b4ed5c2b9136047..43ab0ad45c1b62b8cdd16b956e120dabccc78fee 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
 
+#ifdef CONFIG_CGROUP_DEVICE
+
 static DEFINE_MUTEX(devcgroup_mutex);
 
 enum devcg_behavior {
@@ -792,7 +794,7 @@ struct cgroup_subsys devices_cgrp_subsys = {
 };
 
 /**
- * __devcgroup_check_permission - checks if an inode operation is permitted
+ * devcgroup_legacy_check_permission - checks if an inode operation is permitted
  * @dev_cgroup: the dev cgroup to be tested against
  * @type: device type
  * @major: device major number
@@ -801,7 +803,7 @@ struct cgroup_subsys devices_cgrp_subsys = {
  *
  * returns 0 on success, -EPERM case the operation is not permitted
  */
-static int __devcgroup_check_permission(short type, u32 major, u32 minor,
+static int devcgroup_legacy_check_permission(short type, u32 major, u32 minor,
                                        short access)
 {
        struct dev_cgroup *dev_cgroup;
@@ -825,6 +827,10 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
        return 0;
 }
 
+#endif /* CONFIG_CGROUP_DEVICE */
+
+#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
+
 int devcgroup_check_permission(short type, u32 major, u32 minor, short access)
 {
        int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
@@ -832,6 +838,13 @@ int devcgroup_check_permission(short type, u32 major, u32 minor, short access)
        if (rc)
                return -EPERM;
 
-       return __devcgroup_check_permission(type, major, minor, access);
+       #ifdef CONFIG_CGROUP_DEVICE
+       return devcgroup_legacy_check_permission(type, major, minor, access);
+
+       #else /* CONFIG_CGROUP_DEVICE */
+       return 0;
+
+       #endif /* CONFIG_CGROUP_DEVICE */
 }
 EXPORT_SYMBOL(devcgroup_check_permission);
+#endif /* defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) */
index f6797ba44bf71634b006031d7ad7948cefd35cd1..14cf81d1a30b140a2b0907cd19d2e139e4f0b9d9 100644 (file)
@@ -323,19 +323,6 @@ error:
        return ukey;
 }
 
-static int calc_hash(struct crypto_shash *tfm, u8 *digest,
-                    const u8 *buf, unsigned int buflen)
-{
-       SHASH_DESC_ON_STACK(desc, tfm);
-       int err;
-
-       desc->tfm = tfm;
-
-       err = crypto_shash_digest(desc, buf, buflen, digest);
-       shash_desc_zero(desc);
-       return err;
-}
-
 static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
                     const u8 *buf, unsigned int buflen)
 {
@@ -351,7 +338,7 @@ static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
 
        err = crypto_shash_setkey(tfm, key, keylen);
        if (!err)
-               err = calc_hash(tfm, digest, buf, buflen);
+               err = crypto_shash_tfm_digest(tfm, buf, buflen, digest);
        crypto_free_shash(tfm);
        return err;
 }
@@ -381,7 +368,8 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type,
 
        memcpy(derived_buf + strlen(derived_buf) + 1, master_key,
               master_keylen);
-       ret = calc_hash(hash_tfm, derived_key, derived_buf, derived_buf_len);
+       ret = crypto_shash_tfm_digest(hash_tfm, derived_buf, derived_buf_len,
+                                     derived_key);
        kzfree(derived_buf);
        return ret;
 }
index 986f3ac14282f8a760b5346c7661524fa14209af..d233ab3f153347b0ea25d64e03cb6d7e4afa0756 100644 (file)
@@ -27,9 +27,9 @@
            "audit_control", "setfcap"
 
 #define COMMON_CAP2_PERMS  "mac_override", "mac_admin", "syslog", \
-               "wake_alarm", "block_suspend", "audit_read"
+               "wake_alarm", "block_suspend", "audit_read", "perfmon"
 
-#if CAP_LAST_CAP > CAP_AUDIT_READ
+#if CAP_LAST_CAP > CAP_PERFMON
 #error New capability defined, please update COMMON_CAP2_PERMS.
 #endif
 
index 1b467381986f7f073d6cd62c4dab71493aaeaf60..f93f8acd05f7673fe693fe17c352cbfbecde2cf9 100644 (file)
@@ -2662,8 +2662,6 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
 
        if (!head->write)
                return -EINVAL;
-       if (!access_ok(buffer, buffer_len))
-               return -EFAULT;
        if (mutex_lock_interruptible(&head->io_sem))
                return -EINTR;
        head->read_user_buf_avail = 0;
index b412d3b3d5ffcbf95ba11edc5ab7154548e4b1d9..21edb8ac95eb354d8ec95c2b262cf61eb4db7982 100644 (file)
@@ -216,12 +216,12 @@ static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
        if (info.index >= 32)
                return -EINVAL;
        /* check whether the dsp was already loaded */
-       if (hw->dsp_loaded & (1 << info.index))
+       if (hw->dsp_loaded & (1u << info.index))
                return -EBUSY;
        err = hw->ops.dsp_load(hw, &info);
        if (err < 0)
                return err;
-       hw->dsp_loaded |= (1 << info.index);
+       hw->dsp_loaded |= (1u << info.index);
        return 0;
 }
 
index a83553fbedf00a8c2bf338355d1523dc80be3628..bea46ed157a6d008d8b3bc41fb314fa7f88f9297 100644 (file)
@@ -143,7 +143,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
                break;
        case SNDRV_DMA_TYPE_VMALLOC:
                gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM);
-               dmab->area = __vmalloc(size, gfp, PAGE_KERNEL);
+               dmab->area = __vmalloc(size, gfp);
                dmab->addr = 0;
                break;
 #ifdef CONFIG_HAS_DMA
index fcab37ea66417791c156bdd5dd42d7a18ee41218..860935e3aea492010c78e6274082dc32c5203e63 100644 (file)
@@ -460,7 +460,7 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
                        return 0; /* already large enough */
                vfree(runtime->dma_area);
        }
-       runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+       runtime->dma_area = __vmalloc(size, gfp_flags);
        if (!runtime->dma_area)
                return -ENOMEM;
        runtime->dma_bytes = size;
index aef86025627837621d9fc94ba3b3d9170e2515c4..47838f57a64779e3c99780eb99f39a6914d23c45 100644 (file)
@@ -3093,7 +3093,8 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
                result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
        else
                result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
-       __put_user(result, &_xferi->result);
+       if (put_user(result, &_xferi->result))
+               return -EFAULT;
        return result < 0 ? result : 0;
 }
 
@@ -3122,7 +3123,8 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
        else
                result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
        kfree(bufs);
-       __put_user(result, &_xfern->result);
+       if (put_user(result, &_xfern->result))
+               return -EFAULT;
        return result < 0 ? result : 0;
 }
 
@@ -3137,7 +3139,8 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
        if (put_user(0, _frames))
                return -EFAULT;
        result = snd_pcm_rewind(substream, frames);
-       __put_user(result, _frames);
+       if (put_user(result, _frames))
+               return -EFAULT;
        return result < 0 ? result : 0;
 }
 
@@ -3152,7 +3155,8 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
        if (put_user(0, _frames))
                return -EFAULT;
        result = snd_pcm_forward(substream, frames);
-       __put_user(result, _frames);
+       if (put_user(result, _frames))
+               return -EFAULT;
        return result < 0 ? result : 0;
 }
 
index 041d2a32059b5fec8421e3b09ab0851cf2cf9ee9..e62d58872b6e600c9f12788a8d03a3bde6b9ed0e 100644 (file)
@@ -384,6 +384,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0282:
        case 0x10ec0283:
        case 0x10ec0286:
+       case 0x10ec0287:
        case 0x10ec0288:
        case 0x10ec0285:
        case 0x10ec0298:
@@ -5484,18 +5485,9 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
                { 0x19, 0x21a11010 }, /* dock mic */
                { }
        };
-       /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
-        * the speaker output becomes too low by some reason on Thinkpads with
-        * ALC298 codec
-        */
-       static const hda_nid_t preferred_pairs[] = {
-               0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
-               0
-       };
        struct alc_spec *spec = codec->spec;
 
        if (action == HDA_FIXUP_ACT_PRE_PROBE) {
-               spec->gen.preferred_dacs = preferred_pairs;
                spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
                snd_hda_apply_pincfgs(codec, pincfgs);
        } else if (action == HDA_FIXUP_ACT_INIT) {
@@ -5508,6 +5500,23 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec,
        }
 }
 
+static void alc_fixup_tpt470_dacs(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
+        * the speaker output becomes too low by some reason on Thinkpads with
+        * ALC298 codec
+        */
+       static const hda_nid_t preferred_pairs[] = {
+               0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
+               0
+       };
+       struct alc_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               spec->gen.preferred_dacs = preferred_pairs;
+}
+
 static void alc_shutup_dell_xps13(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -6063,6 +6072,7 @@ enum {
        ALC700_FIXUP_INTEL_REFERENCE,
        ALC274_FIXUP_DELL_BIND_DACS,
        ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
+       ALC298_FIXUP_TPT470_DOCK_FIX,
        ALC298_FIXUP_TPT470_DOCK,
        ALC255_FIXUP_DUMMY_LINEOUT_VERB,
        ALC255_FIXUP_DELL_HEADSET_MIC,
@@ -6994,12 +7004,18 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC274_FIXUP_DELL_BIND_DACS
        },
-       [ALC298_FIXUP_TPT470_DOCK] = {
+       [ALC298_FIXUP_TPT470_DOCK_FIX] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_tpt470_dock,
                .chained = true,
                .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE
        },
+       [ALC298_FIXUP_TPT470_DOCK] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_tpt470_dacs,
+               .chained = true,
+               .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX
+       },
        [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -7638,6 +7654,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
        {.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
        {.id = ALC292_FIXUP_TPT460, .name = "tpt460"},
+       {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"},
        {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"},
        {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
        {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"},
@@ -8276,6 +8293,7 @@ static int patch_alc269(struct hda_codec *codec)
        case 0x10ec0215:
        case 0x10ec0245:
        case 0x10ec0285:
+       case 0x10ec0287:
        case 0x10ec0289:
                spec->codec_variant = ALC269_TYPE_ALC215;
                spec->shutup = alc225_shutup;
@@ -9554,6 +9572,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
index a88d7854513b96ff7dc3f44ccaebfeea2b12ee35..15769f266790e359bb9c962ebda8464bb52f334d 100644 (file)
@@ -1182,6 +1182,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
                        cval->res = 384;
                }
                break;
+       case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */
+               if ((strstr(kctl->id.name, "Playback Volume") != NULL) ||
+                       strstr(kctl->id.name, "Capture Volume") != NULL) {
+                       cval->min >>= 8;
+                       cval->max = 0;
+                       cval->res = 1;
+               }
+               break;
        }
 }
 
index bfdc6ad52785cee310a38c8207b7381c9b7cf5de..9af7aa93f6fabc2d826ae510e97e1f680e8c1719 100644 (file)
@@ -397,6 +397,21 @@ static const struct usbmix_connector_map trx40_mobo_connector_map[] = {
        {}
 };
 
+/* Rear panel + front mic on Gigabyte TRX40 Aorus Master with ALC1220-VB */
+static const struct usbmix_name_map aorus_master_alc1220vb_map[] = {
+       { 17, NULL },                   /* OT, IEC958?, disabled */
+       { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */
+       { 16, "Line Out" },             /* OT */
+       { 22, "Line Out Playback" },    /* FU */
+       { 7, "Line" },                  /* IT */
+       { 19, "Line Capture" },         /* FU */
+       { 8, "Mic" },                   /* IT */
+       { 20, "Mic Capture" },          /* FU */
+       { 9, "Front Mic" },             /* IT */
+       { 21, "Front Mic Capture" },    /* FU */
+       {}
+};
+
 /*
  * Control map entries
  */
@@ -526,6 +541,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .id = USB_ID(0x1b1c, 0x0a42),
                .map = corsair_virtuoso_map,
        },
+       {       /* Gigabyte TRX40 Aorus Master (rear panel + front mic) */
+               .id = USB_ID(0x0414, 0xa001),
+               .map = aorus_master_alc1220vb_map,
+       },
        {       /* Gigabyte TRX40 Aorus Pro WiFi */
                .id = USB_ID(0x0414, 0xa002),
                .map = trx40_mobo_map,
index 6313c30f5c8507743305ec7cc2fbb9a7ce3f6058..eb89902a83bebbf1d442d33714fc325093cf79e3 100644 (file)
@@ -3566,4 +3566,29 @@ ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */
 ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */
 #undef ALC1220_VB_DESKTOP
 
+/* Two entries for Gigabyte TRX40 Aorus Master:
+ * TRX40 Aorus Master has two USB-audio devices, one for the front headphone
+ * with ESS SABRE9218 DAC chip, while another for the rest I/O (the rear
+ * panel and the front mic) with Realtek ALC1220-VB.
+ * Here we provide two distinct names for making UCM profiles easier.
+ */
+{
+       USB_DEVICE(0x0414, 0xa000),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               .vendor_name = "Gigabyte",
+               .product_name = "Aorus Master Front Headphone",
+               .profile_name = "Gigabyte-Aorus-Master-Front-Headphone",
+               .ifnum = QUIRK_NO_INTERFACE
+       }
+},
+{
+       USB_DEVICE(0x0414, 0xa001),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               .vendor_name = "Gigabyte",
+               .product_name = "Aorus Master Main Audio",
+               .profile_name = "Gigabyte-Aorus-Master-Main-Audio",
+               .ifnum = QUIRK_NO_INTERFACE
+       }
+},
+
 #undef USB_DEVICE_VENDOR_SPEC
index 6e060907c163bfdf98e1e200d731a2c181c08732..d25534940bde04f38796e8e36a57626b81f02d03 100644 (file)
@@ -58,8 +58,7 @@
 #define ORC_TYPE_CALL                  0
 #define ORC_TYPE_REGS                  1
 #define ORC_TYPE_REGS_IRET             2
-#define UNWIND_HINT_TYPE_SAVE          3
-#define UNWIND_HINT_TYPE_RESTORE       4
+#define UNWIND_HINT_TYPE_RET_OFFSET    3
 
 #ifndef __ASSEMBLY__
 /*
index 196fdd02b8b1b3d71ac994715f7dee8fa8b5fa52..30d7d04d72d6bad6dfb419f7606e8d5c8fd9b622 100644 (file)
@@ -3,7 +3,7 @@
 #define _UAPI_ASM_X86_UNISTD_H
 
 /* x32 syscall flag bit */
-#define __X32_SYSCALL_BIT      0x40000000UL
+#define __X32_SYSCALL_BIT      0x40000000
 
 #ifndef __KERNEL__
 # ifdef __i386__
index 3e0c019ef2971bb598795a553482568968416372..3abd4316cd4f964758ebc0fd7b7046921da8bf58 100644 (file)
@@ -98,7 +98,8 @@ FEATURE_TESTS_EXTRA :=                  \
          llvm                           \
          llvm-version                   \
          clang                          \
-         libbpf
+         libbpf                         \
+         libpfm4
 
 FEATURE_TESTS ?= $(FEATURE_TESTS_BASIC)
 
index 92012381393ad97e72c4eec27f56c691edec7bac..84f845b9627d3122344e65c08b49efd0be62294f 100644 (file)
@@ -69,7 +69,8 @@ FILES=                                          \
          test-libaio.bin                       \
          test-libzstd.bin                      \
          test-clang-bpf-global-var.bin         \
-         test-file-handle.bin
+         test-file-handle.bin                  \
+         test-libpfm4.bin
 
 FILES := $(addprefix $(OUTPUT),$(FILES))
 
@@ -331,6 +332,9 @@ $(OUTPUT)test-clang-bpf-global-var.bin:
 $(OUTPUT)test-file-handle.bin:
        $(BUILD)
 
+$(OUTPUT)test-libpfm4.bin:
+       $(BUILD) -lpfm
+
 ###############################
 
 clean:
index 2b0e02c3887076aa674357bbbf7ca7c24e1e7bbf..1547bc2c09505ff80d863e0e2909eb77da26b9f4 100644 (file)
@@ -4,9 +4,9 @@
 /*
  * Check OpenCSD library version is sufficient to provide required features
  */
-#define OCSD_MIN_VER ((0 << 16) | (11 << 8) | (0))
+#define OCSD_MIN_VER ((0 << 16) | (14 << 8) | (0))
 #if !defined(OCSD_VER_NUM) || (OCSD_VER_NUM < OCSD_MIN_VER)
-#error "OpenCSD >= 0.11.0 is required"
+#error "OpenCSD >= 0.14.0 is required"
 #endif
 
 int main(void)
diff --git a/tools/build/feature/test-libpfm4.c b/tools/build/feature/test-libpfm4.c
new file mode 100644 (file)
index 0000000..af49b25
--- /dev/null
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <sys/types.h>
+#include <perfmon/pfmlib.h>
+
+int main(void)
+{
+       pfm_initialize();
+       return 0;
+}
index e03b1ea23e0e3ee882cc416e17b4f2afcbdf8b89..30dd21f976c30c7c071dbede368392c83526645d 100644 (file)
@@ -11,7 +11,7 @@
   I know it's not the cleaner way,  but in C (not in C++) to get
   performances and genericity...
 
-  See Documentation/rbtree.txt for documentation and samples.
+  See Documentation/core-api/rbtree.rst for documentation and samples.
 */
 
 #ifndef __TOOLS_LINUX_PERF_RBTREE_H
index 381aa948610da5352cf7c164f5948b1d3e4bc9a0..570bb9794421b975c8d2ae4ba469177d31d86695 100644 (file)
@@ -23,7 +23,7 @@
  * rb_insert_augmented() and rb_erase_augmented() are intended to be public.
  * The rest are implementation details you are not expected to depend on.
  *
- * See Documentation/rbtree.txt for documentation and samples.
+ * See Documentation/core-api/rbtree.rst for documentation and samples.
  */
 
 struct rb_augment_callbacks {
index 428c7dde6b4b3761f9df65ed9181b50e1c9f7165..fdd632c833b4eaee6b48311086d608ba0f692c7f 100644 (file)
@@ -116,7 +116,7 @@ struct kvm_irq_level {
         * ACPI gsi notion of irq.
         * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
         * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
-        * For ARM: See Documentation/virt/kvm/api.txt
+        * For ARM: See Documentation/virt/kvm/api.rst
         */
        union {
                __u32 irq;
@@ -1107,7 +1107,7 @@ struct kvm_xen_hvm_config {
  *
  * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
  * the irqfd to operate in resampling mode for level triggered interrupt
- * emulation.  See Documentation/virt/kvm/api.txt.
+ * emulation.  See Documentation/virt/kvm/api.rst.
  */
 #define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
 
index ad80a5c885d598231ccf207ac48d2d908eaf1b85..d1192783139a5dfff66bd0f7d9b9ee52fc717002 100644 (file)
@@ -148,9 +148,18 @@ struct statx {
 #define STATX_BLOCKS           0x00000400U     /* Want/got stx_blocks */
 #define STATX_BASIC_STATS      0x000007ffU     /* The stuff in the normal stat struct */
 #define STATX_BTIME            0x00000800U     /* Want/got stx_btime */
-#define STATX_ALL              0x00000fffU     /* All currently supported flags */
+
 #define STATX__RESERVED                0x80000000U     /* Reserved for future struct statx expansion */
 
+#ifndef __KERNEL__
+/*
+ * This is deprecated, and shall remain the same value in the future.  To avoid
+ * confusion please use the equivalent (STATX_BASIC_STATS | STATX_BTIME)
+ * instead.
+ */
+#define STATX_ALL              0x00000fffU
+#endif
+
 /*
  * Attributes to be found in stx_attributes and masked in stx_attributes_mask.
  *
index 027b18f7ed8cfd169319c08db2e68239b9ab9333..82f53d81a7a78411c1b6b132a1fcceb9bfc9e9ab 100644 (file)
@@ -90,6 +90,7 @@ struct fs {
        const char * const      *mounts;
        char                     path[PATH_MAX];
        bool                     found;
+       bool                     checked;
        long                     magic;
 };
 
@@ -111,31 +112,37 @@ static struct fs fs__entries[] = {
                .name   = "sysfs",
                .mounts = sysfs__fs_known_mountpoints,
                .magic  = SYSFS_MAGIC,
+               .checked = false,
        },
        [FS__PROCFS] = {
                .name   = "proc",
                .mounts = procfs__known_mountpoints,
                .magic  = PROC_SUPER_MAGIC,
+               .checked = false,
        },
        [FS__DEBUGFS] = {
                .name   = "debugfs",
                .mounts = debugfs__known_mountpoints,
                .magic  = DEBUGFS_MAGIC,
+               .checked = false,
        },
        [FS__TRACEFS] = {
                .name   = "tracefs",
                .mounts = tracefs__known_mountpoints,
                .magic  = TRACEFS_MAGIC,
+               .checked = false,
        },
        [FS__HUGETLBFS] = {
                .name   = "hugetlbfs",
                .mounts = hugetlbfs__known_mountpoints,
                .magic  = HUGETLBFS_MAGIC,
+               .checked = false,
        },
        [FS__BPF_FS] = {
                .name   = "bpf",
                .mounts = bpf_fs__known_mountpoints,
                .magic  = BPF_FS_MAGIC,
+               .checked = false,
        },
 };
 
@@ -158,6 +165,7 @@ static bool fs__read_mounts(struct fs *fs)
        }
 
        fclose(fp);
+       fs->checked = true;
        return fs->found = found;
 }
 
@@ -220,6 +228,7 @@ static bool fs__env_override(struct fs *fs)
                return false;
 
        fs->found = true;
+       fs->checked = true;
        strncpy(fs->path, override_path, sizeof(fs->path) - 1);
        fs->path[sizeof(fs->path) - 1] = '\0';
        return true;
@@ -246,6 +255,14 @@ static const char *fs__mountpoint(int idx)
        if (fs->found)
                return (const char *)fs->path;
 
+       /* the mount point was already checked for the mount point
+        * but and did not exist, so return NULL to avoid scanning again.
+        * This makes the found and not found paths cost equivalent
+        * in case of multiple calls.
+        */
+       if (fs->checked)
+               return NULL;
+
        return fs__get_mountpoint(fs);
 }
 
index 936edb95e1f3c7b418c0f99f347b166607fe0deb..aa222ca30311191b5a64ba8a65010386dced9ebc 100644 (file)
        const char *name##__mount(void);        \
        bool name##__configured(void);          \
 
+/*
+ * The xxxx__mountpoint() entry points find the first match mount point for each
+ * filesystems listed below, where xxxx is the filesystem type.
+ *
+ * The interface is as follows:
+ *
+ * - If a mount point is found on first call, it is cached and used for all
+ *   subsequent calls.
+ *
+ * - If a mount point is not found, NULL is returned on first call and all
+ *   subsequent calls.
+ */
 FS(sysfs)
 FS(procfs)
 FS(debugfs)
diff --git a/tools/lib/api/io.h b/tools/lib/api/io.h
new file mode 100644 (file)
index 0000000..777c20f
--- /dev/null
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lightweight buffered reading library.
+ *
+ * Copyright 2019 Google LLC.
+ */
+#ifndef __API_IO__
+#define __API_IO__
+
+#include <stdlib.h>
+#include <unistd.h>
+
+struct io {
+       /* File descriptor being read/ */
+       int fd;
+       /* Size of the read buffer. */
+       unsigned int buf_len;
+       /* Pointer to storage for buffering read. */
+       char *buf;
+       /* End of the storage. */
+       char *end;
+       /* Currently accessed data pointer. */
+       char *data;
+       /* Set true on when the end of file on read error. */
+       bool eof;
+};
+
+static inline void io__init(struct io *io, int fd,
+                           char *buf, unsigned int buf_len)
+{
+       io->fd = fd;
+       io->buf_len = buf_len;
+       io->buf = buf;
+       io->end = buf;
+       io->data = buf;
+       io->eof = false;
+}
+
+/* Reads one character from the "io" file with similar semantics to fgetc. */
+static inline int io__get_char(struct io *io)
+{
+       char *ptr = io->data;
+
+       if (io->eof)
+               return -1;
+
+       if (ptr == io->end) {
+               ssize_t n = read(io->fd, io->buf, io->buf_len);
+
+               if (n <= 0) {
+                       io->eof = true;
+                       return -1;
+               }
+               ptr = &io->buf[0];
+               io->end = &io->buf[n];
+       }
+       io->data = ptr + 1;
+       return *ptr;
+}
+
+/* Read a hexadecimal value with no 0x prefix into the out argument hex. If the
+ * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise
+ * returns the character after the hexadecimal value which may be -1 for eof.
+ * If the read value is larger than a u64 the high-order bits will be dropped.
+ */
+static inline int io__get_hex(struct io *io, __u64 *hex)
+{
+       bool first_read = true;
+
+       *hex = 0;
+       while (true) {
+               int ch = io__get_char(io);
+
+               if (ch < 0)
+                       return ch;
+               if (ch >= '0' && ch <= '9')
+                       *hex = (*hex << 4) | (ch - '0');
+               else if (ch >= 'a' && ch <= 'f')
+                       *hex = (*hex << 4) | (ch - 'a' + 10);
+               else if (ch >= 'A' && ch <= 'F')
+                       *hex = (*hex << 4) | (ch - 'A' + 10);
+               else if (first_read)
+                       return -2;
+               else
+                       return ch;
+               first_read = false;
+       }
+}
+
+/* Read a positive decimal value with out argument dec. If the first character
+ * isn't a decimal returns -2, io->eof returns -1, otherwise returns the
+ * character after the decimal value which may be -1 for eof. If the read value
+ * is larger than a u64 the high-order bits will be dropped.
+ */
+static inline int io__get_dec(struct io *io, __u64 *dec)
+{
+       bool first_read = true;
+
+       *dec = 0;
+       while (true) {
+               int ch = io__get_char(io);
+
+               if (ch < 0)
+                       return ch;
+               if (ch >= '0' && ch <= '9')
+                       *dec = (*dec * 10) + ch - '0';
+               else if (first_read)
+                       return -2;
+               else
+                       return ch;
+               first_read = false;
+       }
+}
+
+#endif /* __API_IO__ */
index f93f4e703e4c76e44a0341ca1273646fd53ece7d..ca0215047c326af41de0b041b928c2f799dcbb53 100644 (file)
@@ -247,7 +247,7 @@ out:
 
 int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx)
 {
-       if (idx < cpus->nr)
+       if (cpus && idx < cpus->nr)
                return cpus->map[idx];
 
        return -1;
index 5b9f2ca50591dd2a5b5b79d24d4e4bee979c1d84..6a875a0f01bb0056c0569ff478a023f471cddb3d 100644 (file)
 #include <internal/mmap.h>
 #include <internal/cpumap.h>
 #include <internal/threadmap.h>
-#include <internal/xyarray.h>
 #include <internal/lib.h>
 #include <linux/zalloc.h>
-#include <sys/ioctl.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -125,8 +123,10 @@ static void perf_evlist__purge(struct perf_evlist *evlist)
 void perf_evlist__exit(struct perf_evlist *evlist)
 {
        perf_cpu_map__put(evlist->cpus);
+       perf_cpu_map__put(evlist->all_cpus);
        perf_thread_map__put(evlist->threads);
        evlist->cpus = NULL;
+       evlist->all_cpus = NULL;
        evlist->threads = NULL;
        fdarray__exit(&evlist->pollfd);
 }
index af9def58986300acdbc09d891bf40274b787d2f2..d2414144eb8c9927eb9ac7fa2824d848e62e0fd1 100644 (file)
@@ -151,6 +151,8 @@ struct option {
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) }
+#define OPT_CALLBACK_SET(s, l, v, os, a, h, f) \
+       { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .set = check_vtype(os, bool *)}
 #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
 #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
index 1a7a9f87709587dfc5d8d11226f1555cc4e30ad7..e335ac2b9e19725790b347e5aee4a30d9f04e738 100644 (file)
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "symbol/kallsyms.h"
+#include "api/io.h"
 #include <stdio.h>
-#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 u8 kallsyms2elf_type(char type)
 {
@@ -15,74 +17,62 @@ bool kallsyms__is_function(char symbol_type)
        return symbol_type == 'T' || symbol_type == 'W';
 }
 
-/*
- * While we find nice hex chars, build a long_val.
- * Return number of chars processed.
- */
-int hex2u64(const char *ptr, u64 *long_val)
+static void read_to_eol(struct io *io)
 {
-       char *p;
+       int ch;
 
-       *long_val = strtoull(ptr, &p, 16);
-
-       return p - ptr;
+       for (;;) {
+               ch = io__get_char(io);
+               if (ch < 0 || ch == '\n')
+                       return;
+       }
 }
 
 int kallsyms__parse(const char *filename, void *arg,
                    int (*process_symbol)(void *arg, const char *name,
                                          char type, u64 start))
 {
-       char *line = NULL;
-       size_t n;
-       int err = -1;
-       FILE *file = fopen(filename, "r");
-
-       if (file == NULL)
-               goto out_failure;
-
-       err = 0;
+       struct io io;
+       char bf[BUFSIZ];
+       int err;
 
-       while (!feof(file)) {
-               u64 start;
-               int line_len, len;
-               char symbol_type;
-               char *symbol_name;
+       io.fd = open(filename, O_RDONLY, 0);
 
-               line_len = getline(&line, &n, file);
-               if (line_len < 0 || !line)
-                       break;
+       if (io.fd < 0)
+               return -1;
 
-               line[--line_len] = '\0'; /* \n */
+       io__init(&io, io.fd, bf, sizeof(bf));
 
-               len = hex2u64(line, &start);
+       err = 0;
+       while (!io.eof) {
+               __u64 start;
+               int ch;
+               size_t i;
+               char symbol_type;
+               char symbol_name[KSYM_NAME_LEN + 1];
 
-               /* Skip the line if we failed to parse the address. */
-               if (!len)
+               if (io__get_hex(&io, &start) != ' ') {
+                       read_to_eol(&io);
                        continue;
-
-               len++;
-               if (len + 2 >= line_len)
+               }
+               symbol_type = io__get_char(&io);
+               if (io__get_char(&io) != ' ') {
+                       read_to_eol(&io);
                        continue;
-
-               symbol_type = line[len];
-               len += 2;
-               symbol_name = line + len;
-               len = line_len - len;
-
-               if (len >= KSYM_NAME_LEN) {
-                       err = -1;
-                       break;
                }
+               for (i = 0; i < sizeof(symbol_name); i++) {
+                       ch = io__get_char(&io);
+                       if (ch < 0 || ch == '\n')
+                               break;
+                       symbol_name[i]  = ch;
+               }
+               symbol_name[i]  = '\0';
 
                err = process_symbol(arg, symbol_name, symbol_type, start);
                if (err)
                        break;
        }
 
-       free(line);
-       fclose(file);
+       close(io.fd);
        return err;
-
-out_failure:
-       return -1;
 }
index bd988f7b18d4a12d69d2eb43c0710ea266642e72..72ab9870454baf15052fabb233ebcc04b27f94e3 100644 (file)
@@ -18,8 +18,6 @@ static inline u8 kallsyms2elf_binding(char type)
        return isupper(type) ? STB_GLOBAL : STB_LOCAL;
 }
 
-int hex2u64(const char *ptr, u64 *long_val);
-
 u8 kallsyms2elf_type(char type);
 
 bool kallsyms__is_function(char symbol_type);
index b887e7437d67423aa48209ff1826f16915f67ba4..27f3b07fdae8bed2a5f02118b2933cca82ac46f0 100644 (file)
@@ -438,7 +438,7 @@ void *kbuffer_translate_data(int swap, void *data, unsigned int *size)
        case KBUFFER_TYPE_TIME_EXTEND:
        case KBUFFER_TYPE_TIME_STAMP:
                return NULL;
-       };
+       }
 
        *size = length;
 
index 20eed719542e5fcb9388d33cb93635db11c30f5e..c271aeeb227dd5b24e5f6bd48894bd9ac3cc28e0 100644 (file)
@@ -1958,7 +1958,8 @@ static char *op_to_str(struct tep_event_filter *filter, struct tep_filter_arg *a
                                default:
                                        break;
                                }
-                               asprintf(&str, val ? "TRUE" : "FALSE");
+                               if (asprintf(&str, val ? "TRUE" : "FALSE") < 0)
+                                       str = NULL;
                                break;
                        }
                }
@@ -1976,7 +1977,8 @@ static char *op_to_str(struct tep_event_filter *filter, struct tep_filter_arg *a
                        break;
                }
 
-               asprintf(&str, "(%s) %s (%s)", left, op, right);
+               if (asprintf(&str, "(%s) %s (%s)", left, op, right) < 0)
+                       str = NULL;
                break;
 
        case TEP_FILTER_OP_NOT:
@@ -1992,10 +1994,12 @@ static char *op_to_str(struct tep_event_filter *filter, struct tep_filter_arg *a
                        right_val = 0;
                if (right_val >= 0) {
                        /* just return the opposite */
-                       asprintf(&str, right_val ? "FALSE" : "TRUE");
+                       if (asprintf(&str, right_val ? "FALSE" : "TRUE") < 0)
+                               str = NULL;
                        break;
                }
-               asprintf(&str, "%s(%s)", op, right);
+               if (asprintf(&str, "%s(%s)", op, right) < 0)
+                       str = NULL;
                break;
 
        default:
@@ -2011,7 +2015,8 @@ static char *val_to_str(struct tep_event_filter *filter, struct tep_filter_arg *
 {
        char *str = NULL;
 
-       asprintf(&str, "%lld", arg->value.val);
+       if (asprintf(&str, "%lld", arg->value.val) < 0)
+               str = NULL;
 
        return str;
 }
@@ -2069,7 +2074,8 @@ static char *exp_to_str(struct tep_event_filter *filter, struct tep_filter_arg *
                break;
        }
 
-       asprintf(&str, "%s %s %s", lstr, op, rstr);
+       if (asprintf(&str, "%s %s %s", lstr, op, rstr) < 0)
+               str = NULL;
 out:
        free(lstr);
        free(rstr);
@@ -2113,7 +2119,8 @@ static char *num_to_str(struct tep_event_filter *filter, struct tep_filter_arg *
                if (!op)
                        op = "<=";
 
-               asprintf(&str, "%s %s %s", lstr, op, rstr);
+               if (asprintf(&str, "%s %s %s", lstr, op, rstr) < 0)
+                       str = NULL;
                break;
 
        default:
@@ -2148,8 +2155,9 @@ static char *str_to_str(struct tep_event_filter *filter, struct tep_filter_arg *
                if (!op)
                        op = "!~";
 
-               asprintf(&str, "%s %s \"%s\"",
-                        arg->str.field->name, op, arg->str.val);
+               if (asprintf(&str, "%s %s \"%s\"",
+                        arg->str.field->name, op, arg->str.val) < 0)
+                       str = NULL;
                break;
 
        default:
@@ -2165,7 +2173,8 @@ static char *arg_to_str(struct tep_event_filter *filter, struct tep_filter_arg *
 
        switch (arg->type) {
        case TEP_FILTER_ARG_BOOLEAN:
-               asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE");
+               if (asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE") < 0)
+                       str = NULL;
                return str;
 
        case TEP_FILTER_ARG_OP:
index 66f44f5cd2a6255da11e87f4c6a5e9d101938b23..b7222d5cc7bc9019867a2982e855d9315cfaa1c8 100644 (file)
@@ -1,11 +1,16 @@
 objtool-y += arch/$(SRCARCH)/
+
+objtool-y += weak.o
+
+objtool-$(SUBCMD_CHECK) += check.o
+objtool-$(SUBCMD_CHECK) += special.o
+objtool-$(SUBCMD_ORC) += check.o
+objtool-$(SUBCMD_ORC) += orc_gen.o
+objtool-$(SUBCMD_ORC) += orc_dump.o
+
 objtool-y += builtin-check.o
 objtool-y += builtin-orc.o
-objtool-y += check.o
-objtool-y += orc_gen.o
-objtool-y += orc_dump.o
 objtool-y += elf.o
-objtool-y += special.o
 objtool-y += objtool.o
 
 objtool-y += libstring.o
index de094670050be01bb1b801bdbacf6fbe17a8676e..0542e46c755286ba8dd85111a8f21f1880ddbf34 100644 (file)
@@ -289,6 +289,47 @@ they mean, and suggestions for how to fix them.
       might be corrupt due to a gcc bug.  For more details, see:
       https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
 
+9. file.o: warning: objtool: funcA() call to funcB() with UACCESS enabled
+
+   This means that an unexpected call to a non-whitelisted function exists
+   outside of arch-specific guards.
+   X86: SMAP (stac/clac): __uaccess_begin()/__uaccess_end()
+   ARM: PAN: uaccess_enable()/uaccess_disable()
+
+   These functions should be called to denote a minimal critical section around
+   access to __user variables. See also: https://lwn.net/Articles/517475/
+
+   The intention of the warning is to prevent calls to funcB() from eventually
+   calling schedule(), potentially leaking the AC flags state, and not
+   restoring them correctly.
+
+   It also helps verify that there are no unexpected calls to funcB() which may
+   access user space pages with protections against doing so disabled.
+
+   To fix, either:
+   1) remove explicit calls to funcB() from funcA().
+   2) add the correct guards before and after calls to low level functions like
+      __get_user_size()/__put_user_size().
+   3) add funcB to uaccess_safe_builtin whitelist in tools/objtool/check.c, if
+      funcB obviously does not call schedule(), and is marked notrace (since
+      function tracing inserts additional calls, which is not obvious from the
+      sources).
+
+10. file.o: warning: func()+0x5c: alternative modifies stack
+
+    This means that an alternative includes instructions that modify the
+    stack. The problem is that there is only one ORC unwind table, this means
+    that the ORC unwind entries must be valid for each of the alternatives.
+    The easiest way to enforce this is to ensure alternatives do not contain
+    any ORC entries, which in turn implies the above constraint.
+
+11. file.o: warning: unannotated intra-function call
+
+   This warning means that a direct call is done to a destination which
+   is not at the beginning of a function. If this is a legit call, you
+   can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
+   directive right before the call.
+
 
 If the error doesn't seem to make sense, it could be a bug in objtool.
 Feel free to ask the objtool maintainer for help.
index f591c4d1b6fe29b0873208705d238399be707527..7770edcda3a0d61e91c184d6ff64ef4e13d091e8 100644 (file)
@@ -35,7 +35,8 @@ all: $(OBJTOOL)
 
 INCLUDES := -I$(srctree)/tools/include \
            -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
-           -I$(srctree)/tools/arch/$(SRCARCH)/include
+           -I$(srctree)/tools/arch/$(SRCARCH)/include  \
+           -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
 WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
 CFLAGS   := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
 LDFLAGS  += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
@@ -45,14 +46,24 @@ elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(CC) $(CFLAGS) -x c -E -
 CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
 
 AWK = awk
+
+SUBCMD_CHECK := n
+SUBCMD_ORC := n
+
+ifeq ($(SRCARCH),x86)
+       SUBCMD_CHECK := y
+       SUBCMD_ORC := y
+endif
+
+export SUBCMD_CHECK SUBCMD_ORC
 export srctree OUTPUT CFLAGS SRCARCH AWK
 include $(srctree)/tools/build/Makefile.include
 
 $(OBJTOOL_IN): fixdep FORCE
+       @$(CONFIG_SHELL) ./sync-check.sh
        @$(MAKE) $(build)=objtool
 
 $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
-       @$(CONFIG_SHELL) ./sync-check.sh
        $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
 
 
index ced3765c4f4445daf3d1d8db2851af6955978ca5..eda15a5a285e9224f554ff9fa91678ca888eaf85 100644 (file)
@@ -8,9 +8,11 @@
 
 #include <stdbool.h>
 #include <linux/list.h>
-#include "elf.h"
+#include "objtool.h"
 #include "cfi.h"
 
+#include <asm/orc_types.h>
+
 enum insn_type {
        INSN_JUMP_CONDITIONAL,
        INSN_JUMP_UNCONDITIONAL,
@@ -20,7 +22,6 @@ enum insn_type {
        INSN_CALL_DYNAMIC,
        INSN_RETURN,
        INSN_CONTEXT_SWITCH,
-       INSN_STACK,
        INSN_BUG,
        INSN_NOP,
        INSN_STAC,
@@ -64,15 +65,23 @@ struct op_src {
 struct stack_op {
        struct op_dest dest;
        struct op_src src;
+       struct list_head list;
 };
 
-void arch_initial_func_cfi_state(struct cfi_state *state);
+struct instruction;
+
+void arch_initial_func_cfi_state(struct cfi_init_state *state);
 
-int arch_decode_instruction(struct elf *elf, struct section *sec,
+int arch_decode_instruction(const struct elf *elf, const struct section *sec,
                            unsigned long offset, unsigned int maxlen,
                            unsigned int *len, enum insn_type *type,
-                           unsigned long *immediate, struct stack_op *op);
+                           unsigned long *immediate,
+                           struct list_head *ops_list);
 
 bool arch_callee_saved_reg(unsigned char reg);
 
+unsigned long arch_jump_destination(struct instruction *insn);
+
+unsigned long arch_dest_rela_offset(int addend);
+
 #endif /* _ARCH_H */
index a62e032863a89dbd2941739317ea2778e752f294..4b504fc90bbb37a50dcb63f103c7214498fd56c4 100644 (file)
@@ -11,6 +11,7 @@
 #include "../../../arch/x86/lib/inat.c"
 #include "../../../arch/x86/lib/insn.c"
 
+#include "../../check.h"
 #include "../../elf.h"
 #include "../../arch.h"
 #include "../../warn.h"
@@ -26,7 +27,7 @@ static unsigned char op_to_cfi_reg[][2] = {
        {CFI_DI, CFI_R15},
 };
 
-static int is_x86_64(struct elf *elf)
+static int is_x86_64(const struct elf *elf)
 {
        switch (elf->ehdr.e_machine) {
        case EM_X86_64:
@@ -66,16 +67,34 @@ bool arch_callee_saved_reg(unsigned char reg)
        }
 }
 
-int arch_decode_instruction(struct elf *elf, struct section *sec,
+unsigned long arch_dest_rela_offset(int addend)
+{
+       return addend + 4;
+}
+
+unsigned long arch_jump_destination(struct instruction *insn)
+{
+       return insn->offset + insn->len + insn->immediate;
+}
+
+#define ADD_OP(op) \
+       if (!(op = calloc(1, sizeof(*op)))) \
+               return -1; \
+       else for (list_add_tail(&op->list, ops_list); op; op = NULL)
+
+int arch_decode_instruction(const struct elf *elf, const struct section *sec,
                            unsigned long offset, unsigned int maxlen,
                            unsigned int *len, enum insn_type *type,
-                           unsigned long *immediate, struct stack_op *op)
+                           unsigned long *immediate,
+                           struct list_head *ops_list)
 {
        struct insn insn;
        int x86_64, sign;
        unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
                      rex_x = 0, modrm = 0, modrm_mod = 0, modrm_rm = 0,
                      modrm_reg = 0, sib = 0;
+       struct stack_op *op = NULL;
+       struct symbol *sym;
 
        x86_64 = is_x86_64(elf);
        if (x86_64 == -1)
@@ -85,7 +104,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
        insn_get_length(&insn);
 
        if (!insn_complete(&insn)) {
-               WARN_FUNC("can't decode instruction", sec, offset);
+               WARN("can't decode instruction at %s:0x%lx", sec->name, offset);
                return -1;
        }
 
@@ -123,40 +142,44 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
 
                        /* add/sub reg, %rsp */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_ADD;
-                       op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_ADD;
+                               op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
                }
                break;
 
        case 0x50 ... 0x57:
 
                /* push reg */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_REG;
-               op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
-               op->dest.type = OP_DEST_PUSH;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_REG;
+                       op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
+                       op->dest.type = OP_DEST_PUSH;
+               }
 
                break;
 
        case 0x58 ... 0x5f:
 
                /* pop reg */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_POP;
-               op->dest.type = OP_DEST_REG;
-               op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_POP;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
+               }
 
                break;
 
        case 0x68:
        case 0x6a:
                /* push immediate */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_CONST;
-               op->dest.type = OP_DEST_PUSH;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_CONST;
+                       op->dest.type = OP_DEST_PUSH;
+               }
                break;
 
        case 0x70 ... 0x7f:
@@ -170,12 +193,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 
                if (modrm == 0xe4) {
                        /* and imm, %rsp */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_AND;
-                       op->src.reg = CFI_SP;
-                       op->src.offset = insn.immediate.value;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_AND;
+                               op->src.reg = CFI_SP;
+                               op->src.offset = insn.immediate.value;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
                        break;
                }
 
@@ -187,34 +211,37 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                        break;
 
                /* add/sub imm, %rsp */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_ADD;
-               op->src.reg = CFI_SP;
-               op->src.offset = insn.immediate.value * sign;
-               op->dest.type = OP_DEST_REG;
-               op->dest.reg = CFI_SP;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_ADD;
+                       op->src.reg = CFI_SP;
+                       op->src.offset = insn.immediate.value * sign;
+                       op->dest.type = OP_DEST_REG;
+                       op->dest.reg = CFI_SP;
+               }
                break;
 
        case 0x89:
                if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) {
 
                        /* mov %rsp, reg */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG;
-                       op->src.reg = CFI_SP;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG;
+                               op->src.reg = CFI_SP;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
+                       }
                        break;
                }
 
                if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
 
                        /* mov reg, %rsp */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG;
-                       op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG;
+                               op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
                        break;
                }
 
@@ -224,22 +251,24 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                    (modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) {
 
                        /* mov reg, disp(%rbp) */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG;
-                       op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
-                       op->dest.type = OP_DEST_REG_INDIRECT;
-                       op->dest.reg = CFI_BP;
-                       op->dest.offset = insn.displacement.value;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG;
+                               op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                               op->dest.type = OP_DEST_REG_INDIRECT;
+                               op->dest.reg = CFI_BP;
+                               op->dest.offset = insn.displacement.value;
+                       }
 
                } else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
 
                        /* mov reg, disp(%rsp) */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG;
-                       op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
-                       op->dest.type = OP_DEST_REG_INDIRECT;
-                       op->dest.reg = CFI_SP;
-                       op->dest.offset = insn.displacement.value;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG;
+                               op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                               op->dest.type = OP_DEST_REG_INDIRECT;
+                               op->dest.reg = CFI_SP;
+                               op->dest.offset = insn.displacement.value;
+                       }
                }
 
                break;
@@ -248,23 +277,25 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) {
 
                        /* mov disp(%rbp), reg */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG_INDIRECT;
-                       op->src.reg = CFI_BP;
-                       op->src.offset = insn.displacement.value;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG_INDIRECT;
+                               op->src.reg = CFI_BP;
+                               op->src.offset = insn.displacement.value;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                       }
 
                } else if (rex_w && !rex_b && sib == 0x24 &&
                           modrm_mod != 3 && modrm_rm == 4) {
 
                        /* mov disp(%rsp), reg */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_REG_INDIRECT;
-                       op->src.reg = CFI_SP;
-                       op->src.offset = insn.displacement.value;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_REG_INDIRECT;
+                               op->src.reg = CFI_SP;
+                               op->src.offset = insn.displacement.value;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
+                       }
                }
 
                break;
@@ -272,28 +303,30 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
        case 0x8d:
                if (sib == 0x24 && rex_w && !rex_b && !rex_x) {
 
-                       *type = INSN_STACK;
-                       if (!insn.displacement.value) {
-                               /* lea (%rsp), reg */
-                               op->src.type = OP_SRC_REG;
-                       } else {
-                               /* lea disp(%rsp), reg */
-                               op->src.type = OP_SRC_ADD;
-                               op->src.offset = insn.displacement.value;
+                       ADD_OP(op) {
+                               if (!insn.displacement.value) {
+                                       /* lea (%rsp), reg */
+                                       op->src.type = OP_SRC_REG;
+                               } else {
+                                       /* lea disp(%rsp), reg */
+                                       op->src.type = OP_SRC_ADD;
+                                       op->src.offset = insn.displacement.value;
+                               }
+                               op->src.reg = CFI_SP;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
                        }
-                       op->src.reg = CFI_SP;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 
                } else if (rex == 0x48 && modrm == 0x65) {
 
                        /* lea disp(%rbp), %rsp */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_ADD;
-                       op->src.reg = CFI_BP;
-                       op->src.offset = insn.displacement.value;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_ADD;
+                               op->src.reg = CFI_BP;
+                               op->src.offset = insn.displacement.value;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
 
                } else if (rex == 0x49 && modrm == 0x62 &&
                           insn.displacement.value == -8) {
@@ -304,12 +337,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                         * Restoring rsp back to its original value after a
                         * stack realignment.
                         */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_ADD;
-                       op->src.reg = CFI_R10;
-                       op->src.offset = -8;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_ADD;
+                               op->src.reg = CFI_R10;
+                               op->src.offset = -8;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
 
                } else if (rex == 0x49 && modrm == 0x65 &&
                           insn.displacement.value == -16) {
@@ -320,21 +354,23 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                         * Restoring rsp back to its original value after a
                         * stack realignment.
                         */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_ADD;
-                       op->src.reg = CFI_R13;
-                       op->src.offset = -16;
-                       op->dest.type = OP_DEST_REG;
-                       op->dest.reg = CFI_SP;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_ADD;
+                               op->src.reg = CFI_R13;
+                               op->src.offset = -16;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
                }
 
                break;
 
        case 0x8f:
                /* pop to mem */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_POP;
-               op->dest.type = OP_DEST_MEM;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_POP;
+                       op->dest.type = OP_DEST_MEM;
+               }
                break;
 
        case 0x90:
@@ -343,16 +379,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 
        case 0x9c:
                /* pushf */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_CONST;
-               op->dest.type = OP_DEST_PUSHF;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_CONST;
+                       op->dest.type = OP_DEST_PUSHF;
+               }
                break;
 
        case 0x9d:
                /* popf */
-               *type = INSN_STACK;
-               op->src.type = OP_SRC_POPF;
-               op->dest.type = OP_DEST_MEM;
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_POPF;
+                       op->dest.type = OP_DEST_MEM;
+               }
                break;
 
        case 0x0f:
@@ -387,16 +425,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                } else if (op2 == 0xa0 || op2 == 0xa8) {
 
                        /* push fs/gs */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_CONST;
-                       op->dest.type = OP_DEST_PUSH;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_CONST;
+                               op->dest.type = OP_DEST_PUSH;
+                       }
 
                } else if (op2 == 0xa1 || op2 == 0xa9) {
 
                        /* pop fs/gs */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_POP;
-                       op->dest.type = OP_DEST_MEM;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_POP;
+                               op->dest.type = OP_DEST_MEM;
+                       }
                }
 
                break;
@@ -409,8 +449,8 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                 * mov bp, sp
                 * pop bp
                 */
-               *type = INSN_STACK;
-               op->dest.type = OP_DEST_LEAVE;
+               ADD_OP(op)
+                       op->dest.type = OP_DEST_LEAVE;
 
                break;
 
@@ -429,14 +469,41 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                *type = INSN_RETURN;
                break;
 
+       case 0xcf: /* iret */
+               /*
+                * Handle sync_core(), which has an IRET to self.
+                * All other IRET are in STT_NONE entry code.
+                */
+               sym = find_symbol_containing(sec, offset);
+               if (sym && sym->type == STT_FUNC) {
+                       ADD_OP(op) {
+                               /* add $40, %rsp */
+                               op->src.type = OP_SRC_ADD;
+                               op->src.reg = CFI_SP;
+                               op->src.offset = 5*8;
+                               op->dest.type = OP_DEST_REG;
+                               op->dest.reg = CFI_SP;
+                       }
+                       break;
+               }
+
+               /* fallthrough */
+
        case 0xca: /* retf */
        case 0xcb: /* retf */
-       case 0xcf: /* iret */
                *type = INSN_CONTEXT_SWITCH;
                break;
 
        case 0xe8:
                *type = INSN_CALL;
+               /*
+                * For the impact on the stack, a CALL behaves like
+                * a PUSH of an immediate value (the return address).
+                */
+               ADD_OP(op) {
+                       op->src.type = OP_SRC_CONST;
+                       op->dest.type = OP_DEST_PUSH;
+               }
                break;
 
        case 0xfc:
@@ -464,9 +531,10 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
                else if (modrm_reg == 6) {
 
                        /* push from mem */
-                       *type = INSN_STACK;
-                       op->src.type = OP_SRC_CONST;
-                       op->dest.type = OP_DEST_PUSH;
+                       ADD_OP(op) {
+                               op->src.type = OP_SRC_CONST;
+                               op->dest.type = OP_DEST_PUSH;
+                       }
                }
 
                break;
@@ -480,7 +548,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
        return 0;
 }
 
-void arch_initial_func_cfi_state(struct cfi_state *state)
+void arch_initial_func_cfi_state(struct cfi_init_state *state)
 {
        int i;
 
diff --git a/tools/objtool/arch/x86/include/cfi_regs.h b/tools/objtool/arch/x86/include/cfi_regs.h
new file mode 100644 (file)
index 0000000..79bc517
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _OBJTOOL_CFI_REGS_H
+#define _OBJTOOL_CFI_REGS_H
+
+#define CFI_AX                 0
+#define CFI_DX                 1
+#define CFI_CX                 2
+#define CFI_BX                 3
+#define CFI_SI                 4
+#define CFI_DI                 5
+#define CFI_BP                 6
+#define CFI_SP                 7
+#define CFI_R8                 8
+#define CFI_R9                 9
+#define CFI_R10                        10
+#define CFI_R11                        11
+#define CFI_R12                        12
+#define CFI_R13                        13
+#define CFI_R14                        14
+#define CFI_R15                        15
+#define CFI_RA                 16
+#define CFI_NUM_REGS           17
+
+#endif /* _OBJTOOL_CFI_REGS_H */
index 10fbe75ab43dd36a9d4f546091ffa7cd6e6a8766..7a44174967b5fca316e4c5c98c64f80e360ae8c1 100644 (file)
  */
 
 #include <subcmd/parse-options.h>
+#include <string.h>
 #include "builtin.h"
-#include "check.h"
+#include "objtool.h"
 
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 static const char * const check_usage[] = {
        "objtool check [<options>] file.o",
@@ -32,12 +33,14 @@ const struct option check_options[] = {
        OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
        OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
        OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
+       OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
+       OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
        OPT_END(),
 };
 
 int cmd_check(int argc, const char **argv)
 {
-       const char *objname;
+       const char *objname, *s;
 
        argc = parse_options(argc, argv, check_options, check_usage, 0);
 
@@ -46,5 +49,9 @@ int cmd_check(int argc, const char **argv)
 
        objname = argv[0];
 
+       s = strstr(objname, "vmlinux.o");
+       if (s && !s[9])
+               vmlinux = true;
+
        return check(objname, false);
 }
index 5f7cc6157edd600c3666085c23ed82eca7f5acc4..b1dfe2007962fe1ba1a2e6d9c264da0dca2b1950 100644 (file)
@@ -14,8 +14,7 @@
 
 #include <string.h>
 #include "builtin.h"
-#include "check.h"
-
+#include "objtool.h"
 
 static const char *orc_usage[] = {
        "objtool orc generate [<options>] file.o",
index 0b907902ee790a58ec7d2bd9bc7fd3e78023a1eb..85c979caa36779b5ddc5afb321e9f3c411d7a6ce 100644 (file)
@@ -8,7 +8,7 @@
 #include <subcmd/parse-options.h>
 
 extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
 
 extern int cmd_check(int argc, const char **argv);
 extern int cmd_orc(int argc, const char **argv);
index 4427bf8ed6861320e9db2824a33e1fcae1b01ea9..c7c59c6a44eeacaeff9f0338bf6af83d5476e9f0 100644 (file)
@@ -6,38 +6,33 @@
 #ifndef _OBJTOOL_CFI_H
 #define _OBJTOOL_CFI_H
 
+#include "cfi_regs.h"
+
 #define CFI_UNDEFINED          -1
 #define CFI_CFA                        -2
 #define CFI_SP_INDIRECT                -3
 #define CFI_BP_INDIRECT                -4
 
-#define CFI_AX                 0
-#define CFI_DX                 1
-#define CFI_CX                 2
-#define CFI_BX                 3
-#define CFI_SI                 4
-#define CFI_DI                 5
-#define CFI_BP                 6
-#define CFI_SP                 7
-#define CFI_R8                 8
-#define CFI_R9                 9
-#define CFI_R10                        10
-#define CFI_R11                        11
-#define CFI_R12                        12
-#define CFI_R13                        13
-#define CFI_R14                        14
-#define CFI_R15                        15
-#define CFI_RA                 16
-#define CFI_NUM_REGS           17
-
 struct cfi_reg {
        int base;
        int offset;
 };
 
-struct cfi_state {
+struct cfi_init_state {
+       struct cfi_reg regs[CFI_NUM_REGS];
        struct cfi_reg cfa;
+};
+
+struct cfi_state {
        struct cfi_reg regs[CFI_NUM_REGS];
+       struct cfi_reg vals[CFI_NUM_REGS];
+       struct cfi_reg cfa;
+       int stack_size;
+       int drap_reg, drap_offset;
+       unsigned char type;
+       bool bp_scratch;
+       bool drap;
+       bool end;
 };
 
 #endif /* _OBJTOOL_CFI_H */
index 3c6da70e6084144505fd7016b3568d2d951f615d..63d65a7029005e66484c021b199933c96f630a18 100644 (file)
@@ -7,10 +7,10 @@
 #include <stdlib.h>
 
 #include "builtin.h"
+#include "cfi.h"
+#include "arch.h"
 #include "check.h"
-#include "elf.h"
 #include "special.h"
-#include "arch.h"
 #include "warn.h"
 
 #include <linux/hashtable.h>
@@ -27,16 +27,17 @@ struct alternative {
 };
 
 const char *objname;
-struct cfi_state initial_func_cfi;
+struct cfi_init_state initial_func_cfi;
 
 struct instruction *find_insn(struct objtool_file *file,
                              struct section *sec, unsigned long offset)
 {
        struct instruction *insn;
 
-       hash_for_each_possible(file->insn_hash, insn, hash, offset)
+       hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) {
                if (insn->sec == sec && insn->offset == offset)
                        return insn;
+       }
 
        return NULL;
 }
@@ -226,18 +227,31 @@ static bool dead_end_function(struct objtool_file *file, struct symbol *func)
        return __dead_end_function(file, func, 0);
 }
 
-static void clear_insn_state(struct insn_state *state)
+static void init_cfi_state(struct cfi_state *cfi)
 {
        int i;
 
-       memset(state, 0, sizeof(*state));
-       state->cfa.base = CFI_UNDEFINED;
        for (i = 0; i < CFI_NUM_REGS; i++) {
-               state->regs[i].base = CFI_UNDEFINED;
-               state->vals[i].base = CFI_UNDEFINED;
+               cfi->regs[i].base = CFI_UNDEFINED;
+               cfi->vals[i].base = CFI_UNDEFINED;
        }
-       state->drap_reg = CFI_UNDEFINED;
-       state->drap_offset = -1;
+       cfi->cfa.base = CFI_UNDEFINED;
+       cfi->drap_reg = CFI_UNDEFINED;
+       cfi->drap_offset = -1;
+}
+
+static void init_insn_state(struct insn_state *state, struct section *sec)
+{
+       memset(state, 0, sizeof(*state));
+       init_cfi_state(&state->cfi);
+
+       /*
+        * We need the full vmlinux for noinstr validation, otherwise we can
+        * not correctly determine insn->call_dest->sec (external symbols do
+        * not have a section).
+        */
+       if (vmlinux && sec)
+               state->noinstr = sec->noinstr;
 }
 
 /*
@@ -263,6 +277,10 @@ static int decode_instructions(struct objtool_file *file)
                    strncmp(sec->name, ".discard.", 9))
                        sec->text = true;
 
+               if (!strcmp(sec->name, ".noinstr.text") ||
+                   !strcmp(sec->name, ".entry.text"))
+                       sec->noinstr = true;
+
                for (offset = 0; offset < sec->len; offset += insn->len) {
                        insn = malloc(sizeof(*insn));
                        if (!insn) {
@@ -271,7 +289,8 @@ static int decode_instructions(struct objtool_file *file)
                        }
                        memset(insn, 0, sizeof(*insn));
                        INIT_LIST_HEAD(&insn->alts);
-                       clear_insn_state(&insn->state);
+                       INIT_LIST_HEAD(&insn->stack_ops);
+                       init_cfi_state(&insn->cfi);
 
                        insn->sec = sec;
                        insn->offset = offset;
@@ -280,11 +299,11 @@ static int decode_instructions(struct objtool_file *file)
                                                      sec->len - offset,
                                                      &insn->len, &insn->type,
                                                      &insn->immediate,
-                                                     &insn->stack_op);
+                                                     &insn->stack_ops);
                        if (ret)
                                goto err;
 
-                       hash_add(file->insn_hash, &insn->hash, insn->offset);
+                       hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset));
                        list_add_tail(&insn->list, &file->insn_list);
                        nr_insns++;
                }
@@ -314,6 +333,19 @@ err:
        return ret;
 }
 
+static struct instruction *find_last_insn(struct objtool_file *file,
+                                         struct section *sec)
+{
+       struct instruction *insn = NULL;
+       unsigned int offset;
+       unsigned int end = (sec->len > 10) ? sec->len - 10 : 0;
+
+       for (offset = sec->len - 1; offset >= end && !insn; offset--)
+               insn = find_insn(file, sec, offset);
+
+       return insn;
+}
+
 /*
  * Mark "ud2" instructions and manually annotated dead ends.
  */
@@ -322,7 +354,6 @@ static int add_dead_ends(struct objtool_file *file)
        struct section *sec;
        struct rela *rela;
        struct instruction *insn;
-       bool found;
 
        /*
         * By default, "ud2" is a dead end unless otherwise annotated, because
@@ -348,15 +379,8 @@ static int add_dead_ends(struct objtool_file *file)
                if (insn)
                        insn = list_prev_entry(insn, list);
                else if (rela->addend == rela->sym->sec->len) {
-                       found = false;
-                       list_for_each_entry_reverse(insn, &file->insn_list, list) {
-                               if (insn->sec == rela->sym->sec) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-
-                       if (!found) {
+                       insn = find_last_insn(file, rela->sym->sec);
+                       if (!insn) {
                                WARN("can't find unreachable insn at %s+0x%x",
                                     rela->sym->sec->name, rela->addend);
                                return -1;
@@ -390,15 +414,8 @@ reachable:
                if (insn)
                        insn = list_prev_entry(insn, list);
                else if (rela->addend == rela->sym->sec->len) {
-                       found = false;
-                       list_for_each_entry_reverse(insn, &file->insn_list, list) {
-                               if (insn->sec == rela->sym->sec) {
-                                       found = true;
-                                       break;
-                               }
-                       }
-
-                       if (!found) {
+                       insn = find_last_insn(file, rela->sym->sec);
+                       if (!insn) {
                                WARN("can't find reachable insn at %s+0x%x",
                                     rela->sym->sec->name, rela->addend);
                                return -1;
@@ -490,6 +507,7 @@ static const char *uaccess_safe_builtin[] = {
        "__asan_report_store16_noabort",
        /* KCOV */
        "write_comp_data",
+       "check_kcov_mode",
        "__sanitizer_cov_trace_pc",
        "__sanitizer_cov_trace_const_cmp1",
        "__sanitizer_cov_trace_const_cmp2",
@@ -585,13 +603,14 @@ static int add_jump_destinations(struct objtool_file *file)
                                               insn->offset, insn->len);
                if (!rela) {
                        dest_sec = insn->sec;
-                       dest_off = insn->offset + insn->len + insn->immediate;
+                       dest_off = arch_jump_destination(insn);
                } else if (rela->sym->type == STT_SECTION) {
                        dest_sec = rela->sym->sec;
-                       dest_off = rela->addend + 4;
+                       dest_off = arch_dest_rela_offset(rela->addend);
                } else if (rela->sym->sec->idx) {
                        dest_sec = rela->sym->sec;
-                       dest_off = rela->sym->sym.st_value + rela->addend + 4;
+                       dest_off = rela->sym->sym.st_value +
+                                  arch_dest_rela_offset(rela->addend);
                } else if (strstr(rela->sym->name, "_indirect_thunk_")) {
                        /*
                         * Retpoline jumps are really dynamic jumps in
@@ -665,6 +684,16 @@ static int add_jump_destinations(struct objtool_file *file)
        return 0;
 }
 
+static void remove_insn_ops(struct instruction *insn)
+{
+       struct stack_op *op, *tmp;
+
+       list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
+               list_del(&op->list);
+               free(op);
+       }
+}
+
 /*
  * Find the destination instructions for all calls.
  */
@@ -681,7 +710,7 @@ static int add_call_destinations(struct objtool_file *file)
                rela = find_rela_by_dest_range(file->elf, insn->sec,
                                               insn->offset, insn->len);
                if (!rela) {
-                       dest_off = insn->offset + insn->len + insn->immediate;
+                       dest_off = arch_jump_destination(insn);
                        insn->call_dest = find_func_by_offset(insn->sec, dest_off);
                        if (!insn->call_dest)
                                insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
@@ -690,10 +719,7 @@ static int add_call_destinations(struct objtool_file *file)
                                continue;
 
                        if (!insn->call_dest) {
-                               WARN_FUNC("unsupported intra-function call",
-                                         insn->sec, insn->offset);
-                               if (retpoline)
-                                       WARN("If this is a retpoline, please patch it in with alternatives and annotate it with ANNOTATE_NOSPEC_ALTERNATIVE.");
+                               WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
                                return -1;
                        }
 
@@ -704,17 +730,27 @@ static int add_call_destinations(struct objtool_file *file)
                        }
 
                } else if (rela->sym->type == STT_SECTION) {
+                       dest_off = arch_dest_rela_offset(rela->addend);
                        insn->call_dest = find_func_by_offset(rela->sym->sec,
-                                                             rela->addend+4);
+                                                             dest_off);
                        if (!insn->call_dest) {
-                               WARN_FUNC("can't find call dest symbol at %s+0x%x",
+                               WARN_FUNC("can't find call dest symbol at %s+0x%lx",
                                          insn->sec, insn->offset,
                                          rela->sym->sec->name,
-                                         rela->addend + 4);
+                                         dest_off);
                                return -1;
                        }
                } else
                        insn->call_dest = rela->sym;
+
+               /*
+                * Whatever stack impact regular CALLs have, should be undone
+                * by the RETURN of the called function.
+                *
+                * Annotated intra-function calls retain the stack_ops but
+                * are converted to JUMP, see read_intra_function_calls().
+                */
+               remove_insn_ops(insn);
        }
 
        return 0;
@@ -742,7 +778,9 @@ static int handle_group_alt(struct objtool_file *file,
                            struct instruction *orig_insn,
                            struct instruction **new_insn)
 {
+       static unsigned int alt_group_next_index = 1;
        struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL;
+       unsigned int alt_group = alt_group_next_index++;
        unsigned long dest_off;
 
        last_orig_insn = NULL;
@@ -751,7 +789,7 @@ static int handle_group_alt(struct objtool_file *file,
                if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
                        break;
 
-               insn->alt_group = true;
+               insn->alt_group = alt_group;
                last_orig_insn = insn;
        }
 
@@ -763,7 +801,8 @@ static int handle_group_alt(struct objtool_file *file,
                }
                memset(fake_jump, 0, sizeof(*fake_jump));
                INIT_LIST_HEAD(&fake_jump->alts);
-               clear_insn_state(&fake_jump->state);
+               INIT_LIST_HEAD(&fake_jump->stack_ops);
+               init_cfi_state(&fake_jump->cfi);
 
                fake_jump->sec = special_alt->new_sec;
                fake_jump->offset = FAKE_JUMP_OFFSET;
@@ -784,6 +823,7 @@ static int handle_group_alt(struct objtool_file *file,
        }
 
        last_new_insn = NULL;
+       alt_group = alt_group_next_index++;
        insn = *new_insn;
        sec_for_each_insn_from(file, insn) {
                if (insn->offset >= special_alt->new_off + special_alt->new_len)
@@ -793,6 +833,7 @@ static int handle_group_alt(struct objtool_file *file,
 
                insn->ignore = orig_insn->ignore_alts;
                insn->func = orig_insn->func;
+               insn->alt_group = alt_group;
 
                /*
                 * Since alternative replacement code is copy/pasted by the
@@ -821,7 +862,7 @@ static int handle_group_alt(struct objtool_file *file,
                if (!insn->immediate)
                        continue;
 
-               dest_off = insn->offset + insn->len + insn->immediate;
+               dest_off = arch_jump_destination(insn);
                if (dest_off == special_alt->new_off + special_alt->new_len) {
                        if (!fake_jump) {
                                WARN("%s: alternative jump to end of section",
@@ -916,6 +957,12 @@ static int add_special_section_alts(struct objtool_file *file)
                }
 
                if (special_alt->group) {
+                       if (!special_alt->orig_len) {
+                               WARN_FUNC("empty alternative entry",
+                                         orig_insn->sec, orig_insn->offset);
+                               continue;
+                       }
+
                        ret = handle_group_alt(file, special_alt, orig_insn,
                                               &new_insn);
                        if (ret)
@@ -1253,15 +1300,10 @@ static int read_unwind_hints(struct objtool_file *file)
                        return -1;
                }
 
-               cfa = &insn->state.cfa;
-
-               if (hint->type == UNWIND_HINT_TYPE_SAVE) {
-                       insn->save = true;
-                       continue;
+               cfa = &insn->cfi.cfa;
 
-               } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
-                       insn->restore = true;
-                       insn->hint = true;
+               if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
+                       insn->ret_offset = hint->sp_offset;
                        continue;
                }
 
@@ -1299,8 +1341,8 @@ static int read_unwind_hints(struct objtool_file *file)
                }
 
                cfa->offset = hint->sp_offset;
-               insn->state.type = hint->type;
-               insn->state.end = hint->end;
+               insn->cfi.type = hint->type;
+               insn->cfi.end = hint->end;
        }
 
        return 0;
@@ -1341,6 +1383,104 @@ static int read_retpoline_hints(struct objtool_file *file)
        return 0;
 }
 
+static int read_instr_hints(struct objtool_file *file)
+{
+       struct section *sec;
+       struct instruction *insn;
+       struct rela *rela;
+
+       sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("bad .discard.instr_end entry");
+                       return -1;
+               }
+
+               insn->instr--;
+       }
+
+       sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s", sec->name);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("bad .discard.instr_begin entry");
+                       return -1;
+               }
+
+               insn->instr++;
+       }
+
+       return 0;
+}
+
+static int read_intra_function_calls(struct objtool_file *file)
+{
+       struct instruction *insn;
+       struct section *sec;
+       struct rela *rela;
+
+       sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
+       if (!sec)
+               return 0;
+
+       list_for_each_entry(rela, &sec->rela_list, list) {
+               unsigned long dest_off;
+
+               if (rela->sym->type != STT_SECTION) {
+                       WARN("unexpected relocation symbol type in %s",
+                            sec->name);
+                       return -1;
+               }
+
+               insn = find_insn(file, rela->sym->sec, rela->addend);
+               if (!insn) {
+                       WARN("bad .discard.intra_function_call entry");
+                       return -1;
+               }
+
+               if (insn->type != INSN_CALL) {
+                       WARN_FUNC("intra_function_call not a direct call",
+                                 insn->sec, insn->offset);
+                       return -1;
+               }
+
+               /*
+                * Treat intra-function CALLs as JMPs, but with a stack_op.
+                * See add_call_destinations(), which strips stack_ops from
+                * normal CALLs.
+                */
+               insn->type = INSN_JUMP_UNCONDITIONAL;
+
+               dest_off = insn->offset + insn->len + insn->immediate;
+               insn->jump_dest = find_insn(file, insn->sec, dest_off);
+               if (!insn->jump_dest) {
+                       WARN_FUNC("can't find call dest at %s+0x%lx",
+                                 insn->sec, insn->offset,
+                                 insn->sec->name, dest_off);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
 static void mark_rodata(struct objtool_file *file)
 {
        struct section *sec;
@@ -1357,8 +1497,8 @@ static void mark_rodata(struct objtool_file *file)
         * .rodata.str1.* sections are ignored; they don't contain jump tables.
         */
        for_each_sec(file, sec) {
-               if ((!strncmp(sec->name, ".rodata", 7) && !strstr(sec->name, ".str1.")) ||
-                   !strcmp(sec->name, C_JUMP_TABLE_SECTION)) {
+               if (!strncmp(sec->name, ".rodata", 7) &&
+                   !strstr(sec->name, ".str1.")) {
                        sec->rodata = true;
                        found = true;
                }
@@ -1396,6 +1536,10 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       ret = read_intra_function_calls(file);
+       if (ret)
+               return ret;
+
        ret = add_call_destinations(file);
        if (ret)
                return ret;
@@ -1412,12 +1556,16 @@ static int decode_sections(struct objtool_file *file)
        if (ret)
                return ret;
 
+       ret = read_instr_hints(file);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
 static bool is_fentry_call(struct instruction *insn)
 {
-       if (insn->type == INSN_CALL &&
+       if (insn->type == INSN_CALL && insn->call_dest &&
            insn->call_dest->type == STT_NOTYPE &&
            !strcmp(insn->call_dest->name, "__fentry__"))
                return true;
@@ -1425,40 +1573,57 @@ static bool is_fentry_call(struct instruction *insn)
        return false;
 }
 
-static bool has_modified_stack_frame(struct insn_state *state)
+static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
 {
+       u8 ret_offset = insn->ret_offset;
+       struct cfi_state *cfi = &state->cfi;
        int i;
 
-       if (state->cfa.base != initial_func_cfi.cfa.base ||
-           state->cfa.offset != initial_func_cfi.cfa.offset ||
-           state->stack_size != initial_func_cfi.cfa.offset ||
-           state->drap)
+       if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
+               return true;
+
+       if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset)
+               return true;
+
+       if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset)
                return true;
 
-       for (i = 0; i < CFI_NUM_REGS; i++)
-               if (state->regs[i].base != initial_func_cfi.regs[i].base ||
-                   state->regs[i].offset != initial_func_cfi.regs[i].offset)
+       /*
+        * If there is a ret offset hint then don't check registers
+        * because a callee-saved register might have been pushed on
+        * the stack.
+        */
+       if (ret_offset)
+               return false;
+
+       for (i = 0; i < CFI_NUM_REGS; i++) {
+               if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
+                   cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
                        return true;
+       }
 
        return false;
 }
 
 static bool has_valid_stack_frame(struct insn_state *state)
 {
-       if (state->cfa.base == CFI_BP && state->regs[CFI_BP].base == CFI_CFA &&
-           state->regs[CFI_BP].offset == -16)
+       struct cfi_state *cfi = &state->cfi;
+
+       if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
+           cfi->regs[CFI_BP].offset == -16)
                return true;
 
-       if (state->drap && state->regs[CFI_BP].base == CFI_BP)
+       if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
                return true;
 
        return false;
 }
 
-static int update_insn_state_regs(struct instruction *insn, struct insn_state *state)
+static int update_cfi_state_regs(struct instruction *insn,
+                                 struct cfi_state *cfi,
+                                 struct stack_op *op)
 {
-       struct cfi_reg *cfa = &state->cfa;
-       struct stack_op *op = &insn->stack_op;
+       struct cfi_reg *cfa = &cfi->cfa;
 
        if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
                return 0;
@@ -1479,20 +1644,19 @@ static int update_insn_state_regs(struct instruction *insn, struct insn_state *s
        return 0;
 }
 
-static void save_reg(struct insn_state *state, unsigned char reg, int base,
-                    int offset)
+static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
 {
        if (arch_callee_saved_reg(reg) &&
-           state->regs[reg].base == CFI_UNDEFINED) {
-               state->regs[reg].base = base;
-               state->regs[reg].offset = offset;
+           cfi->regs[reg].base == CFI_UNDEFINED) {
+               cfi->regs[reg].base = base;
+               cfi->regs[reg].offset = offset;
        }
 }
 
-static void restore_reg(struct insn_state *state, unsigned char reg)
+static void restore_reg(struct cfi_state *cfi, unsigned char reg)
 {
-       state->regs[reg].base = CFI_UNDEFINED;
-       state->regs[reg].offset = 0;
+       cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
+       cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
 }
 
 /*
@@ -1548,11 +1712,11 @@ static void restore_reg(struct insn_state *state, unsigned char reg)
  *   41 5d                     pop    %r13
  *   c3                                retq
  */
-static int update_insn_state(struct instruction *insn, struct insn_state *state)
+static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
+                            struct stack_op *op)
 {
-       struct stack_op *op = &insn->stack_op;
-       struct cfi_reg *cfa = &state->cfa;
-       struct cfi_reg *regs = state->regs;
+       struct cfi_reg *cfa = &cfi->cfa;
+       struct cfi_reg *regs = cfi->regs;
 
        /* stack operations don't make sense with an undefined CFA */
        if (cfa->base == CFI_UNDEFINED) {
@@ -1563,8 +1727,8 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                return 0;
        }
 
-       if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET)
-               return update_insn_state_regs(insn, state);
+       if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
+               return update_cfi_state_regs(insn, cfi, op);
 
        switch (op->dest.type) {
 
@@ -1579,16 +1743,16 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
 
                                /* mov %rsp, %rbp */
                                cfa->base = op->dest.reg;
-                               state->bp_scratch = false;
+                               cfi->bp_scratch = false;
                        }
 
                        else if (op->src.reg == CFI_SP &&
-                                op->dest.reg == CFI_BP && state->drap) {
+                                op->dest.reg == CFI_BP && cfi->drap) {
 
                                /* drap: mov %rsp, %rbp */
                                regs[CFI_BP].base = CFI_BP;
-                               regs[CFI_BP].offset = -state->stack_size;
-                               state->bp_scratch = false;
+                               regs[CFI_BP].offset = -cfi->stack_size;
+                               cfi->bp_scratch = false;
                        }
 
                        else if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
@@ -1603,8 +1767,8 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                                 *   ...
                                 *   mov    %rax, %rsp
                                 */
-                               state->vals[op->dest.reg].base = CFI_CFA;
-                               state->vals[op->dest.reg].offset = -state->stack_size;
+                               cfi->vals[op->dest.reg].base = CFI_CFA;
+                               cfi->vals[op->dest.reg].offset = -cfi->stack_size;
                        }
 
                        else if (op->src.reg == CFI_BP && op->dest.reg == CFI_SP &&
@@ -1615,14 +1779,14 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                                 *
                                 * Restore the original stack pointer (Clang).
                                 */
-                               state->stack_size = -state->regs[CFI_BP].offset;
+                               cfi->stack_size = -cfi->regs[CFI_BP].offset;
                        }
 
                        else if (op->dest.reg == cfa->base) {
 
                                /* mov %reg, %rsp */
                                if (cfa->base == CFI_SP &&
-                                   state->vals[op->src.reg].base == CFI_CFA) {
+                                   cfi->vals[op->src.reg].base == CFI_CFA) {
 
                                        /*
                                         * This is needed for the rare case
@@ -1632,8 +1796,8 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                                         *   ...
                                         *   mov    %rcx, %rsp
                                         */
-                                       cfa->offset = -state->vals[op->src.reg].offset;
-                                       state->stack_size = cfa->offset;
+                                       cfa->offset = -cfi->vals[op->src.reg].offset;
+                                       cfi->stack_size = cfa->offset;
 
                                } else {
                                        cfa->base = CFI_UNDEFINED;
@@ -1647,7 +1811,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                        if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) {
 
                                /* add imm, %rsp */
-                               state->stack_size -= op->src.offset;
+                               cfi->stack_size -= op->src.offset;
                                if (cfa->base == CFI_SP)
                                        cfa->offset -= op->src.offset;
                                break;
@@ -1656,14 +1820,14 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                        if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
 
                                /* lea disp(%rbp), %rsp */
-                               state->stack_size = -(op->src.offset + regs[CFI_BP].offset);
+                               cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset);
                                break;
                        }
 
                        if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
 
                                /* drap: lea disp(%rsp), %drap */
-                               state->drap_reg = op->dest.reg;
+                               cfi->drap_reg = op->dest.reg;
 
                                /*
                                 * lea disp(%rsp), %reg
@@ -1675,25 +1839,25 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                                 *   ...
                                 *   mov    %rcx, %rsp
                                 */
-                               state->vals[op->dest.reg].base = CFI_CFA;
-                               state->vals[op->dest.reg].offset = \
-                                       -state->stack_size + op->src.offset;
+                               cfi->vals[op->dest.reg].base = CFI_CFA;
+                               cfi->vals[op->dest.reg].offset = \
+                                       -cfi->stack_size + op->src.offset;
 
                                break;
                        }
 
-                       if (state->drap && op->dest.reg == CFI_SP &&
-                           op->src.reg == state->drap_reg) {
+                       if (cfi->drap && op->dest.reg == CFI_SP &&
+                           op->src.reg == cfi->drap_reg) {
 
                                 /* drap: lea disp(%drap), %rsp */
                                cfa->base = CFI_SP;
-                               cfa->offset = state->stack_size = -op->src.offset;
-                               state->drap_reg = CFI_UNDEFINED;
-                               state->drap = false;
+                               cfa->offset = cfi->stack_size = -op->src.offset;
+                               cfi->drap_reg = CFI_UNDEFINED;
+                               cfi->drap = false;
                                break;
                        }
 
-                       if (op->dest.reg == state->cfa.base) {
+                       if (op->dest.reg == cfi->cfa.base) {
                                WARN_FUNC("unsupported stack register modification",
                                          insn->sec, insn->offset);
                                return -1;
@@ -1703,18 +1867,18 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
 
                case OP_SRC_AND:
                        if (op->dest.reg != CFI_SP ||
-                           (state->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
-                           (state->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
+                           (cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
+                           (cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
                                WARN_FUNC("unsupported stack pointer realignment",
                                          insn->sec, insn->offset);
                                return -1;
                        }
 
-                       if (state->drap_reg != CFI_UNDEFINED) {
+                       if (cfi->drap_reg != CFI_UNDEFINED) {
                                /* drap: and imm, %rsp */
-                               cfa->base = state->drap_reg;
-                               cfa->offset = state->stack_size = 0;
-                               state->drap = true;
+                               cfa->base = cfi->drap_reg;
+                               cfa->offset = cfi->stack_size = 0;
+                               cfi->drap = true;
                        }
 
                        /*
@@ -1726,57 +1890,55 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
 
                case OP_SRC_POP:
                case OP_SRC_POPF:
-                       if (!state->drap && op->dest.type == OP_DEST_REG &&
-                           op->dest.reg == cfa->base) {
+                       if (!cfi->drap && op->dest.reg == cfa->base) {
 
                                /* pop %rbp */
                                cfa->base = CFI_SP;
                        }
 
-                       if (state->drap && cfa->base == CFI_BP_INDIRECT &&
-                           op->dest.type == OP_DEST_REG &&
-                           op->dest.reg == state->drap_reg &&
-                           state->drap_offset == -state->stack_size) {
+                       if (cfi->drap && cfa->base == CFI_BP_INDIRECT &&
+                           op->dest.reg == cfi->drap_reg &&
+                           cfi->drap_offset == -cfi->stack_size) {
 
                                /* drap: pop %drap */
-                               cfa->base = state->drap_reg;
+                               cfa->base = cfi->drap_reg;
                                cfa->offset = 0;
-                               state->drap_offset = -1;
+                               cfi->drap_offset = -1;
 
-                       } else if (regs[op->dest.reg].offset == -state->stack_size) {
+                       } else if (regs[op->dest.reg].offset == -cfi->stack_size) {
 
                                /* pop %reg */
-                               restore_reg(state, op->dest.reg);
+                               restore_reg(cfi, op->dest.reg);
                        }
 
-                       state->stack_size -= 8;
+                       cfi->stack_size -= 8;
                        if (cfa->base == CFI_SP)
                                cfa->offset -= 8;
 
                        break;
 
                case OP_SRC_REG_INDIRECT:
-                       if (state->drap && op->src.reg == CFI_BP &&
-                           op->src.offset == state->drap_offset) {
+                       if (cfi->drap && op->src.reg == CFI_BP &&
+                           op->src.offset == cfi->drap_offset) {
 
                                /* drap: mov disp(%rbp), %drap */
-                               cfa->base = state->drap_reg;
+                               cfa->base = cfi->drap_reg;
                                cfa->offset = 0;
-                               state->drap_offset = -1;
+                               cfi->drap_offset = -1;
                        }
 
-                       if (state->drap && op->src.reg == CFI_BP &&
+                       if (cfi->drap && op->src.reg == CFI_BP &&
                            op->src.offset == regs[op->dest.reg].offset) {
 
                                /* drap: mov disp(%rbp), %reg */
-                               restore_reg(state, op->dest.reg);
+                               restore_reg(cfi, op->dest.reg);
 
                        } else if (op->src.reg == cfa->base &&
                            op->src.offset == regs[op->dest.reg].offset + cfa->offset) {
 
                                /* mov disp(%rbp), %reg */
                                /* mov disp(%rsp), %reg */
-                               restore_reg(state, op->dest.reg);
+                               restore_reg(cfi, op->dest.reg);
                        }
 
                        break;
@@ -1791,78 +1953,78 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
 
        case OP_DEST_PUSH:
        case OP_DEST_PUSHF:
-               state->stack_size += 8;
+               cfi->stack_size += 8;
                if (cfa->base == CFI_SP)
                        cfa->offset += 8;
 
                if (op->src.type != OP_SRC_REG)
                        break;
 
-               if (state->drap) {
-                       if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
+               if (cfi->drap) {
+                       if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
 
                                /* drap: push %drap */
                                cfa->base = CFI_BP_INDIRECT;
-                               cfa->offset = -state->stack_size;
+                               cfa->offset = -cfi->stack_size;
 
                                /* save drap so we know when to restore it */
-                               state->drap_offset = -state->stack_size;
+                               cfi->drap_offset = -cfi->stack_size;
 
-                       } else if (op->src.reg == CFI_BP && cfa->base == state->drap_reg) {
+                       } else if (op->src.reg == CFI_BP && cfa->base == cfi->drap_reg) {
 
                                /* drap: push %rbp */
-                               state->stack_size = 0;
+                               cfi->stack_size = 0;
 
                        } else if (regs[op->src.reg].base == CFI_UNDEFINED) {
 
                                /* drap: push %reg */
-                               save_reg(state, op->src.reg, CFI_BP, -state->stack_size);
+                               save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
                        }
 
                } else {
 
                        /* push %reg */
-                       save_reg(state, op->src.reg, CFI_CFA, -state->stack_size);
+                       save_reg(cfi, op->src.reg, CFI_CFA, -cfi->stack_size);
                }
 
                /* detect when asm code uses rbp as a scratch register */
                if (!no_fp && insn->func && op->src.reg == CFI_BP &&
                    cfa->base != CFI_BP)
-                       state->bp_scratch = true;
+                       cfi->bp_scratch = true;
                break;
 
        case OP_DEST_REG_INDIRECT:
 
-               if (state->drap) {
-                       if (op->src.reg == cfa->base && op->src.reg == state->drap_reg) {
+               if (cfi->drap) {
+                       if (op->src.reg == cfa->base && op->src.reg == cfi->drap_reg) {
 
                                /* drap: mov %drap, disp(%rbp) */
                                cfa->base = CFI_BP_INDIRECT;
                                cfa->offset = op->dest.offset;
 
                                /* save drap offset so we know when to restore it */
-                               state->drap_offset = op->dest.offset;
+                               cfi->drap_offset = op->dest.offset;
                        }
 
                        else if (regs[op->src.reg].base == CFI_UNDEFINED) {
 
                                /* drap: mov reg, disp(%rbp) */
-                               save_reg(state, op->src.reg, CFI_BP, op->dest.offset);
+                               save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
                        }
 
                } else if (op->dest.reg == cfa->base) {
 
                        /* mov reg, disp(%rbp) */
                        /* mov reg, disp(%rsp) */
-                       save_reg(state, op->src.reg, CFI_CFA,
-                                op->dest.offset - state->cfa.offset);
+                       save_reg(cfi, op->src.reg, CFI_CFA,
+                                op->dest.offset - cfi->cfa.offset);
                }
 
                break;
 
        case OP_DEST_LEAVE:
-               if ((!state->drap && cfa->base != CFI_BP) ||
-                   (state->drap && cfa->base != state->drap_reg)) {
+               if ((!cfi->drap && cfa->base != CFI_BP) ||
+                   (cfi->drap && cfa->base != cfi->drap_reg)) {
                        WARN_FUNC("leave instruction with modified stack frame",
                                  insn->sec, insn->offset);
                        return -1;
@@ -1870,10 +2032,10 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
 
                /* leave (mov %rbp, %rsp; pop %rbp) */
 
-               state->stack_size = -state->regs[CFI_BP].offset - 8;
-               restore_reg(state, CFI_BP);
+               cfi->stack_size = -cfi->regs[CFI_BP].offset - 8;
+               restore_reg(cfi, CFI_BP);
 
-               if (!state->drap) {
+               if (!cfi->drap) {
                        cfa->base = CFI_SP;
                        cfa->offset -= 8;
                }
@@ -1888,7 +2050,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
                }
 
                /* pop mem */
-               state->stack_size -= 8;
+               cfi->stack_size -= 8;
                if (cfa->base == CFI_SP)
                        cfa->offset -= 8;
 
@@ -1903,41 +2065,86 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
        return 0;
 }
 
-static bool insn_state_match(struct instruction *insn, struct insn_state *state)
+static int handle_insn_ops(struct instruction *insn, struct insn_state *state)
+{
+       struct stack_op *op;
+
+       list_for_each_entry(op, &insn->stack_ops, list) {
+               struct cfi_state old_cfi = state->cfi;
+               int res;
+
+               res = update_cfi_state(insn, &state->cfi, op);
+               if (res)
+                       return res;
+
+               if (insn->alt_group && memcmp(&state->cfi, &old_cfi, sizeof(struct cfi_state))) {
+                       WARN_FUNC("alternative modifies stack", insn->sec, insn->offset);
+                       return -1;
+               }
+
+               if (op->dest.type == OP_DEST_PUSHF) {
+                       if (!state->uaccess_stack) {
+                               state->uaccess_stack = 1;
+                       } else if (state->uaccess_stack >> 31) {
+                               WARN_FUNC("PUSHF stack exhausted",
+                                         insn->sec, insn->offset);
+                               return 1;
+                       }
+                       state->uaccess_stack <<= 1;
+                       state->uaccess_stack  |= state->uaccess;
+               }
+
+               if (op->src.type == OP_SRC_POPF) {
+                       if (state->uaccess_stack) {
+                               state->uaccess = state->uaccess_stack & 1;
+                               state->uaccess_stack >>= 1;
+                               if (state->uaccess_stack == 1)
+                                       state->uaccess_stack = 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
 {
-       struct insn_state *state1 = &insn->state, *state2 = state;
+       struct cfi_state *cfi1 = &insn->cfi;
        int i;
 
-       if (memcmp(&state1->cfa, &state2->cfa, sizeof(state1->cfa))) {
+       if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
+
                WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
                          insn->sec, insn->offset,
-                         state1->cfa.base, state1->cfa.offset,
-                         state2->cfa.base, state2->cfa.offset);
+                         cfi1->cfa.base, cfi1->cfa.offset,
+                         cfi2->cfa.base, cfi2->cfa.offset);
 
-       } else if (memcmp(&state1->regs, &state2->regs, sizeof(state1->regs))) {
+       } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) {
                for (i = 0; i < CFI_NUM_REGS; i++) {
-                       if (!memcmp(&state1->regs[i], &state2->regs[i],
+                       if (!memcmp(&cfi1->regs[i], &cfi2->regs[i],
                                    sizeof(struct cfi_reg)))
                                continue;
 
                        WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
                                  insn->sec, insn->offset,
-                                 i, state1->regs[i].base, state1->regs[i].offset,
-                                 i, state2->regs[i].base, state2->regs[i].offset);
+                                 i, cfi1->regs[i].base, cfi1->regs[i].offset,
+                                 i, cfi2->regs[i].base, cfi2->regs[i].offset);
                        break;
                }
 
-       } else if (state1->type != state2->type) {
+       } else if (cfi1->type != cfi2->type) {
+
                WARN_FUNC("stack state mismatch: type1=%d type2=%d",
-                         insn->sec, insn->offset, state1->type, state2->type);
+                         insn->sec, insn->offset, cfi1->type, cfi2->type);
+
+       } else if (cfi1->drap != cfi2->drap ||
+                  (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
+                  (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
 
-       } else if (state1->drap != state2->drap ||
-                (state1->drap && state1->drap_reg != state2->drap_reg) ||
-                (state1->drap && state1->drap_offset != state2->drap_offset)) {
                WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
                          insn->sec, insn->offset,
-                         state1->drap, state1->drap_reg, state1->drap_offset,
-                         state2->drap, state2->drap_reg, state2->drap_offset);
+                         cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
+                         cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
 
        } else
                return true;
@@ -1963,6 +2170,13 @@ static inline const char *call_dest_name(struct instruction *insn)
 
 static int validate_call(struct instruction *insn, struct insn_state *state)
 {
+       if (state->noinstr && state->instr <= 0 &&
+           (!insn->call_dest || !insn->call_dest->sec->noinstr)) {
+               WARN_FUNC("call to %s() leaves .noinstr.text section",
+                               insn->sec, insn->offset, call_dest_name(insn));
+               return 1;
+       }
+
        if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
                WARN_FUNC("call to %s() with UACCESS enabled",
                                insn->sec, insn->offset, call_dest_name(insn));
@@ -1980,7 +2194,7 @@ static int validate_call(struct instruction *insn, struct insn_state *state)
 
 static int validate_sibling_call(struct instruction *insn, struct insn_state *state)
 {
-       if (has_modified_stack_frame(state)) {
+       if (has_modified_stack_frame(insn, state)) {
                WARN_FUNC("sibling call from callable instruction with modified stack frame",
                                insn->sec, insn->offset);
                return 1;
@@ -1991,6 +2205,12 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
 
 static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
 {
+       if (state->noinstr && state->instr > 0) {
+               WARN_FUNC("return with instrumentation enabled",
+                         insn->sec, insn->offset);
+               return 1;
+       }
+
        if (state->uaccess && !func_uaccess_safe(func)) {
                WARN_FUNC("return with UACCESS enabled",
                          insn->sec, insn->offset);
@@ -2009,13 +2229,13 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
                return 1;
        }
 
-       if (func && has_modified_stack_frame(state)) {
+       if (func && has_modified_stack_frame(insn, state)) {
                WARN_FUNC("return with modified stack frame",
                          insn->sec, insn->offset);
                return 1;
        }
 
-       if (state->bp_scratch) {
+       if (state->cfi.bp_scratch) {
                WARN_FUNC("BP used as a scratch register",
                          insn->sec, insn->offset);
                return 1;
@@ -2024,6 +2244,30 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
        return 0;
 }
 
+/*
+ * Alternatives should not contain any ORC entries, this in turn means they
+ * should not contain any CFI ops, which implies all instructions should have
+ * the same same CFI state.
+ *
+ * It is possible to constuct alternatives that have unreachable holes that go
+ * unreported (because they're NOPs), such holes would result in CFI_UNDEFINED
+ * states which then results in ORC entries, which we just said we didn't want.
+ *
+ * Avoid them by copying the CFI entry of the first instruction into the whole
+ * alternative.
+ */
+static void fill_alternative_cfi(struct objtool_file *file, struct instruction *insn)
+{
+       struct instruction *first_insn = insn;
+       int alt_group = insn->alt_group;
+
+       sec_for_each_insn_continue(file, insn) {
+               if (insn->alt_group != alt_group)
+                       break;
+               insn->cfi = first_insn->cfi;
+       }
+}
+
 /*
  * Follow the branch starting at the given instruction, and recursively follow
  * any other branches (jumps).  Meanwhile, track the frame pointer state at
@@ -2031,23 +2275,16 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
  * tools/objtool/Documentation/stack-validation.txt.
  */
 static int validate_branch(struct objtool_file *file, struct symbol *func,
-                          struct instruction *first, struct insn_state state)
+                          struct instruction *insn, struct insn_state state)
 {
        struct alternative *alt;
-       struct instruction *insn, *next_insn;
+       struct instruction *next_insn;
        struct section *sec;
        u8 visited;
        int ret;
 
-       insn = first;
        sec = insn->sec;
 
-       if (insn->alt_group && list_empty(&insn->alts)) {
-               WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
-                         sec, insn->offset);
-               return 1;
-       }
-
        while (1) {
                next_insn = next_insn_same_sec(file, insn);
 
@@ -2065,59 +2302,24 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
                visited = 1 << state.uaccess;
                if (insn->visited) {
-                       if (!insn->hint && !insn_state_match(insn, &state))
+                       if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
                                return 1;
 
                        if (insn->visited & visited)
                                return 0;
                }
 
-               if (insn->hint) {
-                       if (insn->restore) {
-                               struct instruction *save_insn, *i;
-
-                               i = insn;
-                               save_insn = NULL;
-                               sym_for_each_insn_continue_reverse(file, func, i) {
-                                       if (i->save) {
-                                               save_insn = i;
-                                               break;
-                                       }
-                               }
-
-                               if (!save_insn) {
-                                       WARN_FUNC("no corresponding CFI save for CFI restore",
-                                                 sec, insn->offset);
-                                       return 1;
-                               }
-
-                               if (!save_insn->visited) {
-                                       /*
-                                        * Oops, no state to copy yet.
-                                        * Hopefully we can reach this
-                                        * instruction from another branch
-                                        * after the save insn has been
-                                        * visited.
-                                        */
-                                       if (insn == first)
-                                               return 0;
-
-                                       WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
-                                                 sec, insn->offset);
-                                       return 1;
-                               }
-
-                               insn->state = save_insn->state;
-                       }
-
-                       state = insn->state;
+               if (state.noinstr)
+                       state.instr += insn->instr;
 
-               } else
-                       insn->state = state;
+               if (insn->hint)
+                       state.cfi = insn->cfi;
+               else
+                       insn->cfi = state.cfi;
 
                insn->visited |= visited;
 
-               if (!insn->ignore_alts) {
+               if (!insn->ignore_alts && !list_empty(&insn->alts)) {
                        bool skip_orig = false;
 
                        list_for_each_entry(alt, &insn->alts, list) {
@@ -2132,10 +2334,16 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
                                }
                        }
 
+                       if (insn->alt_group)
+                               fill_alternative_cfi(file, insn);
+
                        if (skip_orig)
                                return 0;
                }
 
+               if (handle_insn_ops(insn, &state))
+                       return 1;
+
                switch (insn->type) {
 
                case INSN_RETURN:
@@ -2202,32 +2410,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
                        }
                        return 0;
 
-               case INSN_STACK:
-                       if (update_insn_state(insn, &state))
-                               return 1;
-
-                       if (insn->stack_op.dest.type == OP_DEST_PUSHF) {
-                               if (!state.uaccess_stack) {
-                                       state.uaccess_stack = 1;
-                               } else if (state.uaccess_stack >> 31) {
-                                       WARN_FUNC("PUSHF stack exhausted", sec, insn->offset);
-                                       return 1;
-                               }
-                               state.uaccess_stack <<= 1;
-                               state.uaccess_stack  |= state.uaccess;
-                       }
-
-                       if (insn->stack_op.src.type == OP_SRC_POPF) {
-                               if (state.uaccess_stack) {
-                                       state.uaccess = state.uaccess_stack & 1;
-                                       state.uaccess_stack >>= 1;
-                                       if (state.uaccess_stack == 1)
-                                               state.uaccess_stack = 0;
-                               }
-                       }
-
-                       break;
-
                case INSN_STAC:
                        if (state.uaccess) {
                                WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
@@ -2273,7 +2455,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
                        return 0;
 
                if (!next_insn) {
-                       if (state.cfa.base == CFI_UNDEFINED)
+                       if (state.cfi.cfa.base == CFI_UNDEFINED)
                                return 0;
                        WARN("%s: unexpected end of section", sec->name);
                        return 1;
@@ -2285,24 +2467,34 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
        return 0;
 }
 
-static int validate_unwind_hints(struct objtool_file *file)
+static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
 {
        struct instruction *insn;
-       int ret, warnings = 0;
        struct insn_state state;
+       int ret, warnings = 0;
 
        if (!file->hints)
                return 0;
 
-       clear_insn_state(&state);
+       init_insn_state(&state, sec);
 
-       for_each_insn(file, insn) {
+       if (sec) {
+               insn = find_insn(file, sec, 0);
+               if (!insn)
+                       return 0;
+       } else {
+               insn = list_first_entry(&file->insn_list, typeof(*insn), list);
+       }
+
+       while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) {
                if (insn->hint && !insn->visited) {
                        ret = validate_branch(file, insn->func, insn, state);
                        if (ret && backtrace)
                                BT_FUNC("<=== (hint)", insn);
                        warnings += ret;
                }
+
+               insn = list_next_entry(insn, list);
        }
 
        return warnings;
@@ -2417,43 +2609,69 @@ static bool ignore_unreachable_insn(struct instruction *insn)
        return false;
 }
 
-static int validate_section(struct objtool_file *file, struct section *sec)
+static int validate_symbol(struct objtool_file *file, struct section *sec,
+                          struct symbol *sym, struct insn_state *state)
 {
-       struct symbol *func;
        struct instruction *insn;
-       struct insn_state state;
-       int ret, warnings = 0;
+       int ret;
+
+       if (!sym->len) {
+               WARN("%s() is missing an ELF size annotation", sym->name);
+               return 1;
+       }
+
+       if (sym->pfunc != sym || sym->alias != sym)
+               return 0;
 
-       clear_insn_state(&state);
+       insn = find_insn(file, sec, sym->offset);
+       if (!insn || insn->ignore || insn->visited)
+               return 0;
+
+       state->uaccess = sym->uaccess_safe;
+
+       ret = validate_branch(file, insn->func, insn, *state);
+       if (ret && backtrace)
+               BT_FUNC("<=== (sym)", insn);
+       return ret;
+}
 
-       state.cfa = initial_func_cfi.cfa;
-       memcpy(&state.regs, &initial_func_cfi.regs,
-              CFI_NUM_REGS * sizeof(struct cfi_reg));
-       state.stack_size = initial_func_cfi.cfa.offset;
+static int validate_section(struct objtool_file *file, struct section *sec)
+{
+       struct insn_state state;
+       struct symbol *func;
+       int warnings = 0;
 
        list_for_each_entry(func, &sec->symbol_list, list) {
                if (func->type != STT_FUNC)
                        continue;
 
-               if (!func->len) {
-                       WARN("%s() is missing an ELF size annotation",
-                            func->name);
-                       warnings++;
-               }
+               init_insn_state(&state, sec);
+               state.cfi.cfa = initial_func_cfi.cfa;
+               memcpy(&state.cfi.regs, &initial_func_cfi.regs,
+                      CFI_NUM_REGS * sizeof(struct cfi_reg));
+               state.cfi.stack_size = initial_func_cfi.cfa.offset;
 
-               if (func->pfunc != func || func->alias != func)
-                       continue;
+               warnings += validate_symbol(file, sec, func, &state);
+       }
 
-               insn = find_insn(file, sec, func->offset);
-               if (!insn || insn->ignore || insn->visited)
-                       continue;
+       return warnings;
+}
 
-               state.uaccess = func->uaccess_safe;
+static int validate_vmlinux_functions(struct objtool_file *file)
+{
+       struct section *sec;
+       int warnings = 0;
 
-               ret = validate_branch(file, func, insn, state);
-               if (ret && backtrace)
-                       BT_FUNC("<=== (func)", insn);
-               warnings += ret;
+       sec = find_section_by_name(file->elf, ".noinstr.text");
+       if (sec) {
+               warnings += validate_section(file, sec);
+               warnings += validate_unwind_hints(file, sec);
+       }
+
+       sec = find_section_by_name(file->elf, ".entry.text");
+       if (sec) {
+               warnings += validate_section(file, sec);
+               warnings += validate_unwind_hints(file, sec);
        }
 
        return warnings;
@@ -2464,8 +2682,12 @@ static int validate_functions(struct objtool_file *file)
        struct section *sec;
        int warnings = 0;
 
-       for_each_sec(file, sec)
+       for_each_sec(file, sec) {
+               if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+                       continue;
+
                warnings += validate_section(file, sec);
+       }
 
        return warnings;
 }
@@ -2496,7 +2718,7 @@ int check(const char *_objname, bool orc)
 
        objname = _objname;
 
-       file.elf = elf_read(objname, orc ? O_RDWR : O_RDONLY);
+       file.elf = elf_open_read(objname, orc ? O_RDWR : O_RDONLY);
        if (!file.elf)
                return 1;
 
@@ -2516,6 +2738,15 @@ int check(const char *_objname, bool orc)
        if (list_empty(&file.insn_list))
                goto out;
 
+       if (vmlinux && !validate_dup) {
+               ret = validate_vmlinux_functions(&file);
+               if (ret < 0)
+                       goto out;
+
+               warnings += ret;
+               goto out;
+       }
+
        if (retpoline) {
                ret = validate_retpoline(&file);
                if (ret < 0)
@@ -2528,7 +2759,7 @@ int check(const char *_objname, bool orc)
                goto out;
        warnings += ret;
 
-       ret = validate_unwind_hints(&file);
+       ret = validate_unwind_hints(&file, NULL);
        if (ret < 0)
                goto out;
        warnings += ret;
index f0ce8ffe71359a47515eeb929ef1edadee2f8507..906b5210f7ca0b9d74301f89595d8b9342d42dc1 100644 (file)
@@ -7,22 +7,16 @@
 #define _CHECK_H
 
 #include <stdbool.h>
-#include "elf.h"
 #include "cfi.h"
 #include "arch.h"
-#include "orc.h"
-#include <linux/hashtable.h>
 
 struct insn_state {
-       struct cfi_reg cfa;
-       struct cfi_reg regs[CFI_NUM_REGS];
-       int stack_size;
-       unsigned char type;
-       bool bp_scratch;
-       bool drap, end, uaccess, df;
+       struct cfi_state cfi;
        unsigned int uaccess_stack;
-       int drap_reg, drap_offset;
-       struct cfi_reg vals[CFI_NUM_REGS];
+       bool uaccess;
+       bool df;
+       bool noinstr;
+       s8 instr;
 };
 
 struct instruction {
@@ -33,29 +27,24 @@ struct instruction {
        unsigned int len;
        enum insn_type type;
        unsigned long immediate;
-       bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
+       bool dead_end, ignore, ignore_alts;
+       bool hint;
        bool retpoline_safe;
+       s8 instr;
        u8 visited;
+       u8 ret_offset;
+       int alt_group;
        struct symbol *call_dest;
        struct instruction *jump_dest;
        struct instruction *first_jump_src;
        struct rela *jump_table;
        struct list_head alts;
        struct symbol *func;
-       struct stack_op stack_op;
-       struct insn_state state;
+       struct list_head stack_ops;
+       struct cfi_state cfi;
        struct orc_entry orc;
 };
 
-struct objtool_file {
-       struct elf *elf;
-       struct list_head insn_list;
-       DECLARE_HASHTABLE(insn_hash, 20);
-       bool ignore_unreachables, c_file, hints, rodata;
-};
-
-int check(const char *objname, bool orc);
-
 struct instruction *find_insn(struct objtool_file *file,
                              struct section *sec, unsigned long offset);
 
index c4857fa3f1d18537c2bcbbc37c6f40b966ffd7e1..84225679f96dae65d5f9af3b29bc07e0fc6a9b34 100644 (file)
@@ -27,6 +27,22 @@ static inline u32 str_hash(const char *str)
        return jhash(str, strlen(str), 0);
 }
 
+static inline int elf_hash_bits(void)
+{
+       return vmlinux ? ELF_HASH_BITS : 16;
+}
+
+#define elf_hash_add(hashtable, node, key) \
+       hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())])
+
+static void elf_hash_init(struct hlist_head *table)
+{
+       __hash_init(table, 1U << elf_hash_bits());
+}
+
+#define elf_hash_for_each_possible(name, obj, member, key)                     \
+       hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member)
+
 static void rb_add(struct rb_root *tree, struct rb_node *node,
                   int (*cmp)(struct rb_node *, const struct rb_node *))
 {
@@ -45,7 +61,7 @@ static void rb_add(struct rb_root *tree, struct rb_node *node,
        rb_insert_color(node, tree);
 }
 
-static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
+static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key,
                               int (*cmp)(const void *key, const struct rb_node *))
 {
        struct rb_node *node = tree->rb_node;
@@ -111,11 +127,11 @@ static int symbol_by_offset(const void *key, const struct rb_node *node)
        return 0;
 }
 
-struct section *find_section_by_name(struct elf *elf, const char *name)
+struct section *find_section_by_name(const struct elf *elf, const char *name)
 {
        struct section *sec;
 
-       hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
+       elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
                if (!strcmp(sec->name, name))
                        return sec;
 
@@ -127,7 +143,7 @@ static struct section *find_section_by_index(struct elf *elf,
 {
        struct section *sec;
 
-       hash_for_each_possible(elf->section_hash, sec, hash, idx)
+       elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
                if (sec->idx == idx)
                        return sec;
 
@@ -138,7 +154,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
 {
        struct symbol *sym;
 
-       hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
+       elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
                if (sym->idx == idx)
                        return sym;
 
@@ -173,7 +189,7 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
        return NULL;
 }
 
-struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
+struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
 {
        struct rb_node *node;
 
@@ -201,18 +217,18 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)
        return NULL;
 }
 
-struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
+struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
 {
        struct symbol *sym;
 
-       hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
+       elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
                if (!strcmp(sym->name, name))
                        return sym;
 
        return NULL;
 }
 
-struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+struct rela *find_rela_by_dest_range(const struct elf *elf, struct section *sec,
                                     unsigned long offset, unsigned int len)
 {
        struct rela *rela, *r = NULL;
@@ -224,7 +240,7 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
        sec = sec->rela;
 
        for_offset_range(o, offset, offset + len) {
-               hash_for_each_possible(elf->rela_hash, rela, hash,
+               elf_hash_for_each_possible(elf->rela_hash, rela, hash,
                                       sec_offset_hash(sec, o)) {
                        if (rela->sec != sec)
                                continue;
@@ -241,7 +257,7 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
        return NULL;
 }
 
-struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset)
+struct rela *find_rela_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
 {
        return find_rela_by_dest_range(elf, sec, offset, 1);
 }
@@ -309,8 +325,8 @@ static int read_sections(struct elf *elf)
                sec->len = sec->sh.sh_size;
 
                list_add_tail(&sec->list, &elf->sections);
-               hash_add(elf->section_hash, &sec->hash, sec->idx);
-               hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
+               elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
+               elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
        }
 
        if (stats)
@@ -327,12 +343,14 @@ static int read_sections(struct elf *elf)
 
 static int read_symbols(struct elf *elf)
 {
-       struct section *symtab, *sec;
+       struct section *symtab, *symtab_shndx, *sec;
        struct symbol *sym, *pfunc;
        struct list_head *entry;
        struct rb_node *pnode;
        int symbols_nr, i;
        char *coldstr;
+       Elf_Data *shndx_data = NULL;
+       Elf32_Word shndx;
 
        symtab = find_section_by_name(elf, ".symtab");
        if (!symtab) {
@@ -340,6 +358,10 @@ static int read_symbols(struct elf *elf)
                return -1;
        }
 
+       symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
+       if (symtab_shndx)
+               shndx_data = symtab_shndx->data;
+
        symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
 
        for (i = 0; i < symbols_nr; i++) {
@@ -353,8 +375,9 @@ static int read_symbols(struct elf *elf)
 
                sym->idx = i;
 
-               if (!gelf_getsym(symtab->data, i, &sym->sym)) {
-                       WARN_ELF("gelf_getsym");
+               if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
+                                     &shndx)) {
+                       WARN_ELF("gelf_getsymshndx");
                        goto err;
                }
 
@@ -368,10 +391,13 @@ static int read_symbols(struct elf *elf)
                sym->type = GELF_ST_TYPE(sym->sym.st_info);
                sym->bind = GELF_ST_BIND(sym->sym.st_info);
 
-               if (sym->sym.st_shndx > SHN_UNDEF &&
-                   sym->sym.st_shndx < SHN_LORESERVE) {
-                       sym->sec = find_section_by_index(elf,
-                                                        sym->sym.st_shndx);
+               if ((sym->sym.st_shndx > SHN_UNDEF &&
+                    sym->sym.st_shndx < SHN_LORESERVE) ||
+                   (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
+                       if (sym->sym.st_shndx != SHN_XINDEX)
+                               shndx = sym->sym.st_shndx;
+
+                       sym->sec = find_section_by_index(elf, shndx);
                        if (!sym->sec) {
                                WARN("couldn't find section for symbol %s",
                                     sym->name);
@@ -394,8 +420,8 @@ static int read_symbols(struct elf *elf)
                else
                        entry = &sym->sec->symbol_list;
                list_add(&sym->list, entry);
-               hash_add(elf->symbol_hash, &sym->hash, sym->idx);
-               hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
+               elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
+               elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
        }
 
        if (stats)
@@ -456,6 +482,14 @@ err:
        return -1;
 }
 
+void elf_add_rela(struct elf *elf, struct rela *rela)
+{
+       struct section *sec = rela->sec;
+
+       list_add_tail(&rela->list, &sec->rela_list);
+       elf_hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+}
+
 static int read_relas(struct elf *elf)
 {
        struct section *sec;
@@ -503,8 +537,7 @@ static int read_relas(struct elf *elf)
                                return -1;
                        }
 
-                       list_add_tail(&rela->list, &sec->rela_list);
-                       hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+                       elf_add_rela(elf, rela);
                        nr_rela++;
                }
                max_rela = max(max_rela, nr_rela);
@@ -519,7 +552,7 @@ static int read_relas(struct elf *elf)
        return 0;
 }
 
-struct elf *elf_read(const char *name, int flags)
+struct elf *elf_open_read(const char *name, int flags)
 {
        struct elf *elf;
        Elf_Cmd cmd;
@@ -531,15 +564,16 @@ struct elf *elf_read(const char *name, int flags)
                perror("malloc");
                return NULL;
        }
-       memset(elf, 0, sizeof(*elf));
+       memset(elf, 0, offsetof(struct elf, sections));
 
-       hash_init(elf->symbol_hash);
-       hash_init(elf->symbol_name_hash);
-       hash_init(elf->section_hash);
-       hash_init(elf->section_name_hash);
-       hash_init(elf->rela_hash);
        INIT_LIST_HEAD(&elf->sections);
 
+       elf_hash_init(elf->symbol_hash);
+       elf_hash_init(elf->symbol_name_hash);
+       elf_hash_init(elf->section_hash);
+       elf_hash_init(elf->section_name_hash);
+       elf_hash_init(elf->rela_hash);
+
        elf->fd = open(name, flags);
        if (elf->fd == -1) {
                fprintf(stderr, "objtool: Can't open '%s': %s\n",
@@ -676,8 +710,8 @@ struct section *elf_create_section(struct elf *elf, const char *name,
        shstrtab->changed = true;
 
        list_add_tail(&sec->list, &elf->sections);
-       hash_add(elf->section_hash, &sec->hash, sec->idx);
-       hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
+       elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
+       elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 
        return sec;
 }
@@ -745,7 +779,7 @@ int elf_rebuild_rela_section(struct section *sec)
        return 0;
 }
 
-int elf_write(struct elf *elf)
+int elf_write(const struct elf *elf)
 {
        struct section *sec;
        Elf_Scn *s;
index 12e01ac190ec1179b81216edc5e72df9c9c17edc..f4fe1d6ea3924c5641346dc433edfe7451676f5d 100644 (file)
@@ -39,7 +39,7 @@ struct section {
        char *name;
        int idx;
        unsigned int len;
-       bool changed, text, rodata;
+       bool changed, text, rodata, noinstr;
 };
 
 struct symbol {
@@ -70,17 +70,19 @@ struct rela {
        bool jump_table_start;
 };
 
+#define ELF_HASH_BITS  20
+
 struct elf {
        Elf *elf;
        GElf_Ehdr ehdr;
        int fd;
        char *name;
        struct list_head sections;
-       DECLARE_HASHTABLE(symbol_hash, 20);
-       DECLARE_HASHTABLE(symbol_name_hash, 20);
-       DECLARE_HASHTABLE(section_hash, 16);
-       DECLARE_HASHTABLE(section_name_hash, 16);
-       DECLARE_HASHTABLE(rela_hash, 20);
+       DECLARE_HASHTABLE(symbol_hash, ELF_HASH_BITS);
+       DECLARE_HASHTABLE(symbol_name_hash, ELF_HASH_BITS);
+       DECLARE_HASHTABLE(section_hash, ELF_HASH_BITS);
+       DECLARE_HASHTABLE(section_name_hash, ELF_HASH_BITS);
+       DECLARE_HASHTABLE(rela_hash, ELF_HASH_BITS);
 };
 
 #define OFFSET_STRIDE_BITS     4
@@ -112,22 +114,23 @@ static inline u32 rela_hash(struct rela *rela)
        return sec_offset_hash(rela->sec, rela->offset);
 }
 
-struct elf *elf_read(const char *name, int flags);
-struct section *find_section_by_name(struct elf *elf, const char *name);
+struct elf *elf_open_read(const char *name, int flags);
+struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr);
+struct section *elf_create_rela_section(struct elf *elf, struct section *base);
+void elf_add_rela(struct elf *elf, struct rela *rela);
+int elf_write(const struct elf *elf);
+void elf_close(struct elf *elf);
+
+struct section *find_section_by_name(const struct elf *elf, const char *name);
 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
-struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
-struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset);
-struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
+struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
+struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
+struct rela *find_rela_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
+struct rela *find_rela_by_dest_range(const struct elf *elf, struct section *sec,
                                     unsigned long offset, unsigned int len);
 struct symbol *find_func_containing(struct section *sec, unsigned long offset);
-struct section *elf_create_section(struct elf *elf, const char *name, size_t
-                                  entsize, int nr);
-struct section *elf_create_rela_section(struct elf *elf, struct section *base);
 int elf_rebuild_rela_section(struct section *sec);
-int elf_write(struct elf *elf);
-void elf_close(struct elf *elf);
 
 #define for_each_sec(file, sec)                                                \
        list_for_each_entry(sec, &file->elf->sections, list)
index 0b3528f05053fa5214e7392ecf4c3c1851a923ae..58fdda510653bc216afbb9fad8584860103d9ae5 100644 (file)
@@ -58,7 +58,9 @@ static void cmd_usage(void)
 
        printf("\n");
 
-       exit(129);
+       if (!help)
+               exit(129);
+       exit(0);
 }
 
 static void handle_options(int *argc, const char ***argv)
diff --git a/tools/objtool/objtool.h b/tools/objtool/objtool.h
new file mode 100644 (file)
index 0000000..528028a
--- /dev/null
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020 Matt Helsley <mhelsley@vmware.com>
+ */
+
+#ifndef _OBJTOOL_H
+#define _OBJTOOL_H
+
+#include <stdbool.h>
+#include <linux/list.h>
+#include <linux/hashtable.h>
+
+#include "elf.h"
+
+struct objtool_file {
+       struct elf *elf;
+       struct list_head insn_list;
+       DECLARE_HASHTABLE(insn_hash, 20);
+       bool ignore_unreachables, c_file, hints, rodata;
+};
+
+int check(const char *objname, bool orc);
+int orc_dump(const char *objname);
+int create_orc(struct objtool_file *file);
+int create_orc_sections(struct objtool_file *file);
+
+#endif /* _OBJTOOL_H */
diff --git a/tools/objtool/orc.h b/tools/objtool/orc.h
deleted file mode 100644 (file)
index ee28322..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
- */
-
-#ifndef _ORC_H
-#define _ORC_H
-
-#include <asm/orc_types.h>
-
-struct objtool_file;
-
-int create_orc(struct objtool_file *file);
-int create_orc_sections(struct objtool_file *file);
-
-int orc_dump(const char *objname);
-
-#endif /* _ORC_H */
index ba4cbb1cdd632413aa7b4ca3603b6c2543674908..fca46e006fc2efd1f7284a9b79e60b07f0da232c 100644 (file)
@@ -4,7 +4,8 @@
  */
 
 #include <unistd.h>
-#include "orc.h"
+#include <asm/orc_types.h>
+#include "objtool.h"
 #include "warn.h"
 
 static const char *reg_name(unsigned int reg)
index 4c0dabd280002771f8d28675088d230ed9b70e60..c9549988121ad744e3f9adbc57de1537931cf10e 100644 (file)
@@ -6,7 +6,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "orc.h"
 #include "check.h"
 #include "warn.h"
 
@@ -16,10 +15,10 @@ int create_orc(struct objtool_file *file)
 
        for_each_insn(file, insn) {
                struct orc_entry *orc = &insn->orc;
-               struct cfi_reg *cfa = &insn->state.cfa;
-               struct cfi_reg *bp = &insn->state.regs[CFI_BP];
+               struct cfi_reg *cfa = &insn->cfi.cfa;
+               struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
 
-               orc->end = insn->state.end;
+               orc->end = insn->cfi.end;
 
                if (cfa->base == CFI_UNDEFINED) {
                        orc->sp_reg = ORC_REG_UNDEFINED;
@@ -75,7 +74,7 @@ int create_orc(struct objtool_file *file)
 
                orc->sp_offset = cfa->offset;
                orc->bp_offset = bp->offset;
-               orc->type = insn->state.type;
+               orc->type = insn->cfi.type;
        }
 
        return 0;
@@ -130,8 +129,7 @@ static int create_orc_entry(struct elf *elf, struct section *u_sec, struct secti
        rela->offset = idx * sizeof(int);
        rela->sec = ip_relasec;
 
-       list_add_tail(&rela->list, &ip_relasec->rela_list);
-       hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
+       elf_add_rela(elf, rela);
 
        return 0;
 }
diff --git a/tools/objtool/weak.c b/tools/objtool/weak.c
new file mode 100644 (file)
index 0000000..942ea5e
--- /dev/null
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Matt Helsley <mhelsley@vmware.com>
+ * Weak definitions necessary to compile objtool without
+ * some subcommands (e.g. check, orc).
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include "objtool.h"
+
+#define __weak __attribute__((weak))
+
+#define UNSUPPORTED(name)                                              \
+({                                                                     \
+       fprintf(stderr, "error: objtool: " name " not implemented\n");  \
+       return ENOSYS;                                                  \
+})
+
+const char __weak *objname;
+
+int __weak check(const char *_objname, bool orc)
+{
+       UNSUPPORTED("check subcommand");
+}
+
+int __weak orc_dump(const char *_objname)
+{
+       UNSUPPORTED("orc");
+}
+
+int __weak create_orc(struct objtool_file *file)
+{
+       UNSUPPORTED("orc");
+}
+
+int __weak create_orc_sections(struct objtool_file *file)
+{
+       UNSUPPORTED("orc");
+}
index 31824d5269cc01635dc0e1d785a6013b3230c862..6e54979c2124e6d22a4ad501db8ded3673517cb7 100644 (file)
@@ -48,7 +48,7 @@ man5dir=$(mandir)/man5
 man7dir=$(mandir)/man7
 
 ASCIIDOC=asciidoc
-ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf
+ASCIIDOC_EXTRA += --unsafe -f asciidoc.conf
 ASCIIDOC_HTML = xhtml11
 MANPAGE_XSL = manpage-normal.xsl
 XMLTO_EXTRA =
@@ -59,7 +59,7 @@ HTML_REF = origin/html
 
 ifdef USE_ASCIIDOCTOR
 ASCIIDOC = asciidoctor
-ASCIIDOC_EXTRA = -a compat-mode
+ASCIIDOC_EXTRA += -a compat-mode
 ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
 ASCIIDOC_EXTRA += -a mansource="perf" -a manmanual="perf Manual"
 ASCIIDOC_HTML = xhtml5
index 82ff7dad40c2744d9619715689e060615c1e4439..271484754feefb2f73047990cea61483d608aa5c 100644 (file)
@@ -10,7 +10,9 @@
                e       synthesize error events
                d       create a debug log
                g       synthesize a call chain (use with i or x)
+               G       synthesize a call chain on existing event records
                l       synthesize last branch entries (use with i or x)
+               L       synthesize last branch entries on existing event records
                s       skip initial number of events
 
        The default is all events i.e. the same as --itrace=ibxwpe,
        Also the number of last branch entries (default 64, max. 1024) for
        instructions or transactions events can be specified.
 
+       Similar to options g and l, size may also be specified for options G and L.
+       On x86, note that G and L work poorly when data has been recorded with
+       large PEBS. Refer linkperf:perf-intel-pt[1] man page for details.
+
        It is also possible to skip events generated (instructions, branches, transactions,
        ptwrite, power) at the beginning. This is useful to ignore initialization code.
 
index 0921a3c673815c6a031278be3107f80011eb5c46..bad16512c48d7b38846fff58a1749c9ec54a9f08 100644 (file)
@@ -61,6 +61,9 @@ SUBSYSTEM
 'epoll'::
        Eventpoll (epoll) stressing benchmarks.
 
+'internals'::
+       Benchmark internal perf functionality.
+
 'all'::
        All benchmark subsystems.
 
@@ -214,6 +217,11 @@ Suite for evaluating concurrent epoll_wait calls.
 *ctl*::
 Suite for evaluating multiple epoll_ctl calls.
 
+SUITES FOR 'internals'
+~~~~~~~~~~~~~~~~~~~~~~
+*synthesize*::
+Suite for evaluating perf's event synthesis performance.
+
 SEE ALSO
 --------
 linkperf:perf[1]
index e6150f21267d69a8ec8e34b9d14b8bb91701bf74..2133eb320cb08bc748cf93e26eafa59055f139b9 100644 (file)
@@ -111,6 +111,17 @@ REPORT OPTIONS
 --display::
        Switch to HITM type (rmt, lcl) to display and sort on. Total HITMs as default.
 
+--stitch-lbr::
+       Show callgraph with stitched LBRs, which may have more complete
+       callgraph. The perf.data file must have been obtained using
+       perf c2c record --call-graph lbr.
+       Disabled by default. In common cases with call stack overflows,
+       it can recreate better call stacks than the default lbr call stack
+       output. But this approach is not full proof. There can be cases
+       where it creates incorrect call stacks from incorrect matches.
+       The known limitations include exception handing such as
+       setjmp/longjmp will have calls/returns not match.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
index 456fdcbf26ac67e6f1973c3936a62723d3d5d3b5..eb8b7d42591a63f94f45aac72451deb489474d4a 100644 (file)
@@ -69,22 +69,22 @@ And profiled with 'perf report' e.g.
 To also trace kernel space presents a problem, namely kernel self-modifying
 code.  A fairly good kernel image is available in /proc/kcore but to get an
 accurate image a copy of /proc/kcore needs to be made under the same conditions
-as the data capture.  A script perf-with-kcore can do that, but beware that the
-script makes use of 'sudo' to copy /proc/kcore.  If you have perf installed
-locally from the source tree you can do:
+as the data capture. 'perf record' can make a copy of /proc/kcore if the option
+--kcore is used, but access to /proc/kcore is restricted e.g.
 
-       ~/libexec/perf-core/perf-with-kcore record pt_ls -e intel_pt// -- ls
+       sudo perf record -o pt_ls --kcore -e intel_pt// -- ls
 
-which will create a directory named 'pt_ls' and put the perf.data file and
-copies of /proc/kcore, /proc/kallsyms and /proc/modules into it.  Then to use
-'perf report' becomes:
+which will create a directory named 'pt_ls' and put the perf.data file (named
+simply 'data') and copies of /proc/kcore, /proc/kallsyms and /proc/modules into
+it.  The other tools understand the directory format, so to use 'perf report'
+becomes:
 
-       ~/libexec/perf-core/perf-with-kcore report pt_ls
+       sudo perf report -i pt_ls
 
 Because samples are synthesized after-the-fact, the sampling period can be
 selected for reporting. e.g. sample every microsecond
 
-       ~/libexec/perf-core/perf-with-kcore report pt_ls --itrace=i1usge
+       sudo perf report pt_ls --itrace=i1usge
 
 See the sections below for more information about the --itrace option.
 
@@ -821,7 +821,9 @@ The letters are:
        e       synthesize tracing error events
        d       create a debug log
        g       synthesize a call chain (use with i or x)
+       G       synthesize a call chain on existing event records
        l       synthesize last branch entries (use with i or x)
+       L       synthesize last branch entries on existing event records
        s       skip initial number of events
 
 "Instructions" events look like they were recorded by "perf record -e
@@ -912,6 +914,39 @@ transactions events can be specified. e.g.
 Note that last branch entries are cleared for each sample, so there is no overlap
 from one sample to the next.
 
+The G and L options are designed in particular for sample mode, and work much
+like g and l but add call chain and branch stack to the other selected events
+instead of synthesized events. For example, to record branch-misses events for
+'ls' and then add a call chain derived from the Intel PT trace:
+
+       perf record --aux-sample -e '{intel_pt//u,branch-misses:u}' -- ls
+       perf report --itrace=Ge
+
+Although in fact G is a default for perf report, so that is the same as just:
+
+       perf report
+
+One caveat with the G and L options is that they work poorly with "Large PEBS".
+Large PEBS means PEBS records will be accumulated by hardware and the written
+into the event buffer in one go.  That reduces interrupts, but can give very
+late timestamps.  Because the Intel PT trace is synchronized by timestamps,
+the PEBS events do not match the trace.  Currently, Large PEBS is used only in
+certain circumstances:
+       - hardware supports it
+       - PEBS is used
+       - event period is specified, instead of frequency
+       - the sample type is limited to the following flags:
+               PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_ADDR |
+               PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_STREAM_ID |
+               PERF_SAMPLE_DATA_SRC | PERF_SAMPLE_IDENTIFIER |
+               PERF_SAMPLE_TRANSACTION | PERF_SAMPLE_PHYS_ADDR |
+               PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER |
+               PERF_SAMPLE_PERIOD (and sometimes) | PERF_SAMPLE_TIME
+Because Intel PT sample mode uses a different sample type to the list above,
+Large PEBS is not used with Intel PT sample mode. To avoid Large PEBS in other
+cases, avoid specifying the event period i.e. avoid the 'perf record' -c option,
+--count option, or 'period' config term.
+
 To disable trace decoding entirely, use the option --no-itrace.
 
 It is also possible to skip events generated (instructions, branches, transactions)
index 6345db33c533961ea7f92fea0b8aa3dd5abd4a55..376a50b3452d0cc19d5e00da86572c0abe776494 100644 (file)
@@ -115,6 +115,11 @@ raw encoding of 0x1A8 can be used:
  perf stat -e r1a8 -a sleep 1
  perf record -e r1a8 ...
 
+It's also possible to use pmu syntax:
+
+ perf record -e r1a8 -a sleep 1
+ perf record -e cpu/r1a8/ ...
+
 You should refer to the processor specific documentation for getting these
 details. Some of them are referenced in the SEE ALSO section below.
 
@@ -258,6 +263,9 @@ Normally all events in an event group sample, but with :S only
 the first event (the leader) samples, and it only reads the values of the
 other events in the group.
 
+However, in the case AUX area events (e.g. Intel PT or CoreSight), the AUX
+area event must be the leader, so then the second event samples, not the first.
+
 OPTIONS
 -------
 
index b3f3b3f1c161d23e6a81a1ce6a173b3920e52260..561ef55743e27196efb0e292bf594b68e060b5ea 100644 (file)
@@ -556,6 +556,19 @@ overhead. You can still switch them on with:
 
   --switch-output --no-no-buildid  --no-no-buildid-cache
 
+--switch-output-event::
+Events that will cause the switch of the perf.data file, auto-selecting
+--switch-output=signal, the results are similar as internally the side band
+thread will also send a SIGUSR2 to the main one.
+
+Uses the same syntax as --event, it will just not be recorded, serving only to
+switch the perf.data file as soon as the --switch-output event is processed by
+a separate sideband thread.
+
+This sideband thread is also used to other purposes, like processing the
+PERF_RECORD_BPF_EVENT records as they happen, asking the kernel for extra BPF
+information, etc.
+
 --switch-max-files=N::
 
 When rotating perf.data with --switch-output, only keep N files.
@@ -596,6 +609,10 @@ Make a copy of /proc/kcore and place it into a directory with the perf data file
 Limit the sample data max size, <size> is expected to be a number with
 appended unit character - B/K/M/G
 
+--num-thread-synthesize::
+       The number of threads to run when synthesizing events for existing processes.
+       By default, the number of threads equals 1.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1]
index f569b9ea40027b96448f268fd9cdbc865109fa65..d068103690ccc80b9fd445439b43afdde6a8d3a3 100644 (file)
@@ -488,6 +488,17 @@ include::itrace.txt[]
        This option extends the perf report to show reference callgraphs,
        which collected by reference event, in no callgraph event.
 
+--stitch-lbr::
+       Show callgraph with stitched LBRs, which may have more complete
+       callgraph. The perf.data file must have been obtained using
+       perf record --call-graph lbr.
+       Disabled by default. In common cases with call stack overflows,
+       it can recreate better call stacks than the default lbr call stack
+       output. But this approach is not full proof. There can be cases
+       where it creates incorrect call stacks from incorrect matches.
+       The known limitations include exception handing such as
+       setjmp/longjmp will have calls/returns not match.
+
 --socket-filter::
        Only report the samples on the processor socket that match with this filter
 
index 963487e82edcdfe3dd7947a914edfc4dce5e099b..372dfd110e6d61237d7e40cd017725966d875789 100644 (file)
@@ -440,6 +440,17 @@ include::itrace.txt[]
 --show-on-off-events::
        Show the --switch-on/off events too.
 
+--stitch-lbr::
+       Show callgraph with stitched LBRs, which may have more complete
+       callgraph. The perf.data file must have been obtained using
+       perf record --call-graph lbr.
+       Disabled by default. In common cases with call stack overflows,
+       it can recreate better call stacks than the default lbr call stack
+       output. But this approach is not full proof. There can be cases
+       where it creates incorrect call stacks from incorrect matches.
+       The known limitations include exception handing such as
+       setjmp/longjmp will have calls/returns not match.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
index 4d56586b2fb926232ac754b7485c4dc041525d4e..3fb5028aef089c2bff4d76005c9be45fa72d7d21 100644 (file)
@@ -176,6 +176,8 @@ Print count deltas every N milliseconds (minimum: 1ms)
 The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals.  Use with caution.
        example: 'perf stat -I 1000 -e cycles -a sleep 5'
 
+If the metric exists, it is calculated by the counts generated in this interval and the metric is printed after #.
+
 --interval-count times::
 Print count deltas for fixed number of times.
 This option should be used together with "-I" option.
index 487737a725e97c56ad580f5edacf52f2c5a4c90c..20227dabc208400f44f864b5c40cdd3f3dfefb1f 100644 (file)
@@ -319,6 +319,15 @@ Default is to monitor all CPUS.
        go straight to the histogram browser, just like 'perf top' with no events
        explicitely specified does.
 
+--stitch-lbr::
+       Show callgraph with stitched LBRs, which may have more complete
+       callgraph. The option must be used with --call-graph lbr recording.
+       Disabled by default. In common cases with call stack overflows,
+       it can recreate better call stacks than the default lbr call stack
+       output. But this approach is not full proof. There can be cases
+       where it creates incorrect call stacks from incorrect matches.
+       The known limitations include exception handing such as
+       setjmp/longjmp will have calls/returns not match.
 
 INTERACTIVE PROMPTING KEYS
 --------------------------
index b0152e1095c58af089b8cd41bbc9264bbcdd8aa3..b6472e463284d16df82478292ccd65c148ccb14e 100644 (file)
@@ -373,6 +373,22 @@ struct {
 Indicates that trace contains records of PERF_RECORD_COMPRESSED type
 that have perf_events records in compressed form.
 
+       HEADER_CPU_PMU_CAPS = 28,
+
+       A list of cpu PMU capabilities. The format of data is as below.
+
+struct {
+       u32 nr_cpu_pmu_caps;
+       {
+               char    name[];
+               char    value[];
+       } [nr_cpu_pmu_caps]
+};
+
+
+Example:
+ cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
+
        other bits are reserved and should ignored for now
        HEADER_FEAT_BITS        = 256,
 
index d15a311408f1c1709c4e4375a475b94ccbaa29ad..94a495594e990af4d8a2aea1a112fa351d71235c 100644 (file)
@@ -188,7 +188,7 @@ AWK     = awk
 # non-config cases
 config := 1
 
-NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help install-doc install-man install-html install-info install-pdf doc man html info pdf
+NON_CONFIG_TARGETS := clean python-clean TAGS tags cscope help
 
 ifdef MAKECMDGOALS
 ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
@@ -832,7 +832,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
 
 # 'make doc' should call 'make -C Documentation all'
 $(DOC_TARGETS):
-       $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all)
+       $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:doc=all) ASCIIDOC_EXTRA=$(ASCIIDOC_EXTRA)
 
 TAG_FOLDERS= . ../lib ../include
 TAG_FILES= ../../include/uapi/linux/perf_event.h
@@ -959,7 +959,7 @@ install-python_ext:
 
 # 'make install-doc' should call 'make -C Documentation install'
 $(INSTALL_DOC_TARGETS):
-       $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:-doc=)
+       $(Q)$(MAKE) -C $(DOC_DIR) O=$(OUTPUT) $(@:-doc=) ASCIIDOC_EXTRA=$(ASCIIDOC_EXTRA)
 
 ### Cleaning rules
 
index 941f814820b8c653930e03d690ac6647227ef552..97aa02c4491d1a2d99a02654b7352fcbe3ab4315 100644 (file)
@@ -23,6 +23,7 @@
 #include "../../util/event.h"
 #include "../../util/evlist.h"
 #include "../../util/evsel.h"
+#include "../../util/perf_api_probe.h"
 #include "../../util/evsel_config.h"
 #include "../../util/pmu.h"
 #include "../../util/cs-etm.h"
@@ -232,7 +233,7 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
                ret = perf_pmu__scan_file(pmu, path, "%x", &hash);
                if (ret != 1) {
                        pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n",
-                              sink, perf_evsel__name(evsel), errno,
+                              sink, evsel__name(evsel), errno,
                               str_error_r(errno, msg, sizeof(msg)));
                        return ret;
                }
@@ -401,7 +402,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
         * when a context switch happened.
         */
        if (!perf_cpu_map__empty(cpus)) {
-               perf_evsel__set_sample_bit(cs_etm_evsel, CPU);
+               evsel__set_sample_bit(cs_etm_evsel, CPU);
 
                err = cs_etm_set_option(itr, cs_etm_evsel,
                                        ETM_OPT_CTXTID | ETM_OPT_TS);
@@ -425,7 +426,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
 
                /* In per-cpu case, always need the time of mmap events etc */
                if (!perf_cpu_map__empty(cpus))
-                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
+                       evsel__set_sample_bit(tracking_evsel, TIME);
        }
 
 out:
index 27653be244473b08701a917dad2ff72c871e1081..e3593063b3d17879cc514f06a8911e5943eb22ad 100644 (file)
@@ -120,9 +120,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
         */
        perf_evlist__to_front(evlist, arm_spe_evsel);
 
-       perf_evsel__set_sample_bit(arm_spe_evsel, CPU);
-       perf_evsel__set_sample_bit(arm_spe_evsel, TIME);
-       perf_evsel__set_sample_bit(arm_spe_evsel, TID);
+       evsel__set_sample_bit(arm_spe_evsel, CPU);
+       evsel__set_sample_bit(arm_spe_evsel, TIME);
+       evsel__set_sample_bit(arm_spe_evsel, TID);
 
        /* Add dummy event to keep tracking */
        err = parse_events(evlist, "dummy:u", NULL);
@@ -134,9 +134,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
 
        tracking_evsel->core.attr.freq = 0;
        tracking_evsel->core.attr.sample_period = 1;
-       perf_evsel__set_sample_bit(tracking_evsel, TIME);
-       perf_evsel__set_sample_bit(tracking_evsel, CPU);
-       perf_evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
+       evsel__set_sample_bit(tracking_evsel, TIME);
+       evsel__set_sample_bit(tracking_evsel, CPU);
+       evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
 
        return 0;
 }
index 3b4cdfc5efd65bce12d74b125f5c6dfd85af639e..d4870074f14c714fa409e5f738ad186f3a0942e7 100644 (file)
@@ -7,6 +7,8 @@
 #include <string.h>
 #include <linux/stringify.h>
 #include "header.h"
+#include "metricgroup.h"
+#include <api/fs/fs.h>
 
 #define mfspr(rn)       ({unsigned long rval; \
                         asm volatile("mfspr %0," __stringify(rn) \
@@ -44,3 +46,9 @@ get_cpuid_str(struct perf_pmu *pmu __maybe_unused)
 
        return bufp;
 }
+
+int arch_get_runtimeparam(void)
+{
+       int count;
+       return sysfs__read_int("/devices/hv_24x7/interface/sockets", &count) < 0 ? 1 : count;
+}
index 16807269317c66458f3be933bafc1329cce4e224..eed9e5a429358789f3c9414b3b3a655e94d4ec48 100644 (file)
@@ -39,7 +39,7 @@ static void hcall_event_get_key(struct evsel *evsel,
                                struct event_key *key)
 {
        key->info = 0;
-       key->key = perf_evsel__intval(evsel, sample, "req");
+       key->key = evsel__intval(evsel, sample, "req");
 }
 
 static const char *get_hcall_exit_reason(u64 exit_code)
index 0fd4e9f49ed01eb13879687bda7de26a2e8b8157..34da89ced29acd12a06d67ad0ee709e80ddf53d8 100644 (file)
@@ -30,7 +30,7 @@ static void event_icpt_insn_get_key(struct evsel *evsel,
 {
        unsigned long insn;
 
-       insn = perf_evsel__intval(evsel, sample, "instruction");
+       insn = evsel__intval(evsel, sample, "instruction");
        key->key = icpt_insn_decoder(insn);
        key->exit_reasons = sie_icpt_insn_codes;
 }
@@ -39,7 +39,7 @@ static void event_sigp_get_key(struct evsel *evsel,
                               struct perf_sample *sample,
                               struct event_key *key)
 {
-       key->key = perf_evsel__intval(evsel, sample, "order_code");
+       key->key = evsel__intval(evsel, sample, "order_code");
        key->exit_reasons = sie_sigp_order_codes;
 }
 
@@ -47,7 +47,7 @@ static void event_diag_get_key(struct evsel *evsel,
                               struct perf_sample *sample,
                               struct event_key *key)
 {
-       key->key = perf_evsel__intval(evsel, sample, "code");
+       key->key = evsel__intval(evsel, sample, "code");
        key->exit_reasons = sie_diagnose_codes;
 }
 
@@ -55,7 +55,7 @@ static void event_icpt_prog_get_key(struct evsel *evsel,
                                    struct perf_sample *sample,
                                    struct event_key *key)
 {
-       key->key = perf_evsel__intval(evsel, sample, "code");
+       key->key = evsel__intval(evsel, sample, "code");
        key->exit_reasons = sie_icpt_prog_codes;
 }
 
index 909ead08a6f6e1397d786d698845cb76f1174db0..026d32ed078e6e436c743ba008831ed9958ae6a1 100644 (file)
@@ -130,13 +130,11 @@ int test__perf_time_to_tsc(struct test *test __maybe_unused, int subtest __maybe
                                goto next_event;
 
                        if (strcmp(event->comm.comm, comm1) == 0) {
-                               CHECK__(perf_evsel__parse_sample(evsel, event,
-                                                                &sample));
+                               CHECK__(evsel__parse_sample(evsel, event, &sample));
                                comm1_time = sample.time;
                        }
                        if (strcmp(event->comm.comm, comm2) == 0) {
-                               CHECK__(perf_evsel__parse_sample(evsel, event,
-                                                                &sample));
+                               CHECK__(evsel__parse_sample(evsel, event, &sample));
                                comm2_time = sample.time;
                        }
 next_event:
index 09f93800bffd0cbdc5bddc6406fb7eebca7acc3c..0dc09b5809c122f6c3d85ef60f5187ea93a38220 100644 (file)
@@ -224,7 +224,7 @@ static int intel_bts_recording_options(struct auxtrace_record *itr,
                 * AUX event.
                 */
                if (!perf_cpu_map__empty(cpus))
-                       perf_evsel__set_sample_bit(intel_bts_evsel, CPU);
+                       evsel__set_sample_bit(intel_bts_evsel, CPU);
        }
 
        /* Add dummy event to keep tracking */
index 1643aed8c4c8ee319206916ad9f1ebfc772a7784..3f7c20cc7b798429fcb56742c87e2d569185a969 100644 (file)
@@ -25,6 +25,7 @@
 #include "../../../util/pmu.h"
 #include "../../../util/debug.h"
 #include "../../../util/auxtrace.h"
+#include "../../../util/perf_api_probe.h"
 #include "../../../util/record.h"
 #include "../../../util/target.h"
 #include "../../../util/tsc.h"
@@ -420,8 +421,8 @@ static int intel_pt_track_switches(struct evlist *evlist)
 
        evsel = evlist__last(evlist);
 
-       perf_evsel__set_sample_bit(evsel, CPU);
-       perf_evsel__set_sample_bit(evsel, TIME);
+       evsel__set_sample_bit(evsel, CPU);
+       evsel__set_sample_bit(evsel, TIME);
 
        evsel->core.system_wide = true;
        evsel->no_aux_samples = true;
@@ -801,10 +802,10 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
                                switch_evsel->no_aux_samples = true;
                                switch_evsel->immediate = true;
 
-                               perf_evsel__set_sample_bit(switch_evsel, TID);
-                               perf_evsel__set_sample_bit(switch_evsel, TIME);
-                               perf_evsel__set_sample_bit(switch_evsel, CPU);
-                               perf_evsel__reset_sample_bit(switch_evsel, BRANCH_STACK);
+                               evsel__set_sample_bit(switch_evsel, TID);
+                               evsel__set_sample_bit(switch_evsel, TIME);
+                               evsel__set_sample_bit(switch_evsel, CPU);
+                               evsel__reset_sample_bit(switch_evsel, BRANCH_STACK);
 
                                opts->record_switch_events = false;
                                ptr->have_sched_switch = 3;
@@ -838,7 +839,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
                 * AUX event.
                 */
                if (!perf_cpu_map__empty(cpus))
-                       perf_evsel__set_sample_bit(intel_pt_evsel, CPU);
+                       evsel__set_sample_bit(intel_pt_evsel, CPU);
        }
 
        /* Add dummy event to keep tracking */
@@ -862,11 +863,11 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
 
                /* In per-cpu case, always need the time of mmap events etc */
                if (!perf_cpu_map__empty(cpus)) {
-                       perf_evsel__set_sample_bit(tracking_evsel, TIME);
+                       evsel__set_sample_bit(tracking_evsel, TIME);
                        /* And the CPU for switch events */
-                       perf_evsel__set_sample_bit(tracking_evsel, CPU);
+                       evsel__set_sample_bit(tracking_evsel, CPU);
                }
-               perf_evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
+               evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
        }
 
        /*
index c0775c39227f0aab7674e2b19c603e689f978220..072920475b659d106594de7a06896f31829c0609 100644 (file)
@@ -31,8 +31,8 @@ const char *kvm_exit_trace = "kvm:kvm_exit";
 static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample,
                               struct event_key *key)
 {
-       key->key  = perf_evsel__intval(evsel, sample, "gpa");
-       key->info = perf_evsel__intval(evsel, sample, "type");
+       key->key  = evsel__intval(evsel, sample, "gpa");
+       key->info = evsel__intval(evsel, sample, "type");
 }
 
 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0
@@ -48,7 +48,7 @@ static bool mmio_event_begin(struct evsel *evsel,
 
        /* MMIO write begin event in kernel. */
        if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
-           perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
+           evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
                mmio_event_get_key(evsel, sample, key);
                return true;
        }
@@ -65,7 +65,7 @@ static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample,
 
        /* MMIO read end event in kernel.*/
        if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
-           perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
+           evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
                mmio_event_get_key(evsel, sample, key);
                return true;
        }
@@ -94,8 +94,8 @@ static void ioport_event_get_key(struct evsel *evsel,
                                 struct perf_sample *sample,
                                 struct event_key *key)
 {
-       key->key  = perf_evsel__intval(evsel, sample, "port");
-       key->info = perf_evsel__intval(evsel, sample, "rw");
+       key->key  = evsel__intval(evsel, sample, "port");
+       key->info = evsel__intval(evsel, sample, "rw");
 }
 
 static bool ioport_event_begin(struct evsel *evsel,
index e4e321b6f8835e88a6d3fe28e18459aeed9ccb9a..768e408757a05632c676e4e1e538ec7097ddd0e6 100644 (file)
@@ -6,9 +6,10 @@ perf-y += futex-wake.o
 perf-y += futex-wake-parallel.o
 perf-y += futex-requeue.o
 perf-y += futex-lock-pi.o
-
 perf-y += epoll-wait.o
 perf-y += epoll-ctl.o
+perf-y += synthesize.o
+perf-y += kallsyms-parse.o
 
 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-lib.o
 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
index 4aa6de1aa67dc6a7f095d135e95f8b406cc7f5c1..61cae4966cae28575ef747c31414926fe82637e2 100644 (file)
@@ -41,9 +41,10 @@ int bench_futex_wake_parallel(int argc, const char **argv);
 int bench_futex_requeue(int argc, const char **argv);
 /* pi futexes */
 int bench_futex_lock_pi(int argc, const char **argv);
-
 int bench_epoll_wait(int argc, const char **argv);
 int bench_epoll_ctl(int argc, const char **argv);
+int bench_synthesize(int argc, const char **argv);
+int bench_kallsyms_parse(int argc, const char **argv);
 
 #define BENCH_FORMAT_DEFAULT_STR       "default"
 #define BENCH_FORMAT_DEFAULT           0
index f938c585d51248ddfc3277383098c05f5a8df60f..cf797362675ba86bf3c2fa2e1015bddc9b8be7a2 100644 (file)
@@ -519,7 +519,8 @@ int bench_epoll_wait(int argc, const char **argv)
                qsort(worker, nthreads, sizeof(struct worker), cmpworker);
 
        for (i = 0; i < nthreads; i++) {
-               unsigned long t = worker[i].ops / bench__runtime.tv_sec;
+               unsigned long t = bench__runtime.tv_sec > 0 ?
+                       worker[i].ops / bench__runtime.tv_sec : 0;
 
                update_stats(&throughput_stats, t);
 
index 65eebe06c04d406b2fa11c5c43d443675af12b88..915bf3da7ce222673d033da5343ba816df791e51 100644 (file)
@@ -205,7 +205,8 @@ int bench_futex_hash(int argc, const char **argv)
        pthread_mutex_destroy(&thread_lock);
 
        for (i = 0; i < nthreads; i++) {
-               unsigned long t = worker[i].ops / bench__runtime.tv_sec;
+               unsigned long t = bench__runtime.tv_sec > 0 ?
+                       worker[i].ops / bench__runtime.tv_sec : 0;
                update_stats(&throughput_stats, t);
                if (!silent) {
                        if (nfutexes == 1)
index 89fd8f325f384eeac2f5e71c246b1e9aff2f28eb..bb25d8beb3b852f71def15136fcc5f22de816aca 100644 (file)
@@ -211,7 +211,8 @@ int bench_futex_lock_pi(int argc, const char **argv)
        pthread_mutex_destroy(&thread_lock);
 
        for (i = 0; i < nthreads; i++) {
-               unsigned long t = worker[i].ops / bench__runtime.tv_sec;
+               unsigned long t = bench__runtime.tv_sec > 0 ?
+                       worker[i].ops / bench__runtime.tv_sec : 0;
 
                update_stats(&throughput_stats, t);
                if (!silent)
diff --git a/tools/perf/bench/kallsyms-parse.c b/tools/perf/bench/kallsyms-parse.c
new file mode 100644 (file)
index 0000000..2b0d0f9
--- /dev/null
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark of /proc/kallsyms parsing.
+ *
+ * Copyright 2020 Google LLC.
+ */
+#include <stdlib.h>
+#include "bench.h"
+#include "../util/stat.h"
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include <symbol/kallsyms.h>
+
+static unsigned int iterations = 100;
+
+static const struct option options[] = {
+       OPT_UINTEGER('i', "iterations", &iterations,
+               "Number of iterations used to compute average"),
+       OPT_END()
+};
+
+static const char *const bench_usage[] = {
+       "perf bench internals kallsyms-parse <options>",
+       NULL
+};
+
+static int bench_process_symbol(void *arg __maybe_unused,
+                               const char *name __maybe_unused,
+                               char type __maybe_unused,
+                               u64 start __maybe_unused)
+{
+       return 0;
+}
+
+static int do_kallsyms_parse(void)
+{
+       struct timeval start, end, diff;
+       u64 runtime_us;
+       unsigned int i;
+       double time_average, time_stddev;
+       int err;
+       struct stats time_stats;
+
+       init_stats(&time_stats);
+
+       for (i = 0; i < iterations; i++) {
+               gettimeofday(&start, NULL);
+               err = kallsyms__parse("/proc/kallsyms", NULL,
+                               bench_process_symbol);
+               if (err)
+                       return err;
+
+               gettimeofday(&end, NULL);
+               timersub(&end, &start, &diff);
+               runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+               update_stats(&time_stats, runtime_us);
+       }
+
+       time_average = avg_stats(&time_stats) / USEC_PER_MSEC;
+       time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC;
+       printf("  Average kallsyms__parse took: %.3f ms (+- %.3f ms)\n",
+               time_average, time_stddev);
+       return 0;
+}
+
+int bench_kallsyms_parse(int argc, const char **argv)
+{
+       argc = parse_options(argc, argv, options, bench_usage, 0);
+       if (argc) {
+               usage_with_options(bench_usage, options);
+               exit(EXIT_FAILURE);
+       }
+
+       return do_kallsyms_parse();
+}
diff --git a/tools/perf/bench/synthesize.c b/tools/perf/bench/synthesize.c
new file mode 100644 (file)
index 0000000..8d624ae
--- /dev/null
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark synthesis of perf events such as at the start of a 'perf
+ * record'. Synthesis is done on the current process and the 'dummy' event
+ * handlers are invoked that support dump_trace but otherwise do nothing.
+ *
+ * Copyright 2019 Google LLC.
+ */
+#include <stdio.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/session.h"
+#include "../util/stat.h"
+#include "../util/synthetic-events.h"
+#include "../util/target.h"
+#include "../util/thread_map.h"
+#include "../util/tool.h"
+#include "../util/util.h"
+#include <linux/atomic.h>
+#include <linux/err.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+
+static unsigned int min_threads = 1;
+static unsigned int max_threads = UINT_MAX;
+static unsigned int single_iterations = 10000;
+static unsigned int multi_iterations = 10;
+static bool run_st;
+static bool run_mt;
+
+static const struct option options[] = {
+       OPT_BOOLEAN('s', "st", &run_st, "Run single threaded benchmark"),
+       OPT_BOOLEAN('t', "mt", &run_mt, "Run multi-threaded benchmark"),
+       OPT_UINTEGER('m', "min-threads", &min_threads,
+               "Minimum number of threads in multithreaded bench"),
+       OPT_UINTEGER('M', "max-threads", &max_threads,
+               "Maximum number of threads in multithreaded bench"),
+       OPT_UINTEGER('i', "single-iterations", &single_iterations,
+               "Number of iterations used to compute single-threaded average"),
+       OPT_UINTEGER('I', "multi-iterations", &multi_iterations,
+               "Number of iterations used to compute multi-threaded average"),
+       OPT_END()
+};
+
+static const char *const bench_usage[] = {
+       "perf bench internals synthesize <options>",
+       NULL
+};
+
+static atomic_t event_count;
+
+static int process_synthesized_event(struct perf_tool *tool __maybe_unused,
+                                    union perf_event *event __maybe_unused,
+                                    struct perf_sample *sample __maybe_unused,
+                                    struct machine *machine __maybe_unused)
+{
+       atomic_inc(&event_count);
+       return 0;
+}
+
+static int do_run_single_threaded(struct perf_session *session,
+                               struct perf_thread_map *threads,
+                               struct target *target, bool data_mmap)
+{
+       const unsigned int nr_threads_synthesize = 1;
+       struct timeval start, end, diff;
+       u64 runtime_us;
+       unsigned int i;
+       double time_average, time_stddev, event_average, event_stddev;
+       int err;
+       struct stats time_stats, event_stats;
+
+       init_stats(&time_stats);
+       init_stats(&event_stats);
+
+       for (i = 0; i < single_iterations; i++) {
+               atomic_set(&event_count, 0);
+               gettimeofday(&start, NULL);
+               err = __machine__synthesize_threads(&session->machines.host,
+                                               NULL,
+                                               target, threads,
+                                               process_synthesized_event,
+                                               data_mmap,
+                                               nr_threads_synthesize);
+               if (err)
+                       return err;
+
+               gettimeofday(&end, NULL);
+               timersub(&end, &start, &diff);
+               runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+               update_stats(&time_stats, runtime_us);
+               update_stats(&event_stats, atomic_read(&event_count));
+       }
+
+       time_average = avg_stats(&time_stats);
+       time_stddev = stddev_stats(&time_stats);
+       printf("  Average %ssynthesis took: %.3f usec (+- %.3f usec)\n",
+               data_mmap ? "data " : "", time_average, time_stddev);
+
+       event_average = avg_stats(&event_stats);
+       event_stddev = stddev_stats(&event_stats);
+       printf("  Average num. events: %.3f (+- %.3f)\n",
+               event_average, event_stddev);
+
+       printf("  Average time per event %.3f usec\n",
+               time_average / event_average);
+       return 0;
+}
+
+static int run_single_threaded(void)
+{
+       struct perf_session *session;
+       struct target target = {
+               .pid = "self",
+       };
+       struct perf_thread_map *threads;
+       int err;
+
+       perf_set_singlethreaded();
+       session = perf_session__new(NULL, false, NULL);
+       if (IS_ERR(session)) {
+               pr_err("Session creation failed.\n");
+               return PTR_ERR(session);
+       }
+       threads = thread_map__new_by_pid(getpid());
+       if (!threads) {
+               pr_err("Thread map creation failed.\n");
+               err = -ENOMEM;
+               goto err_out;
+       }
+
+       puts(
+"Computing performance of single threaded perf event synthesis by\n"
+"synthesizing events on the perf process itself:");
+
+       err = do_run_single_threaded(session, threads, &target, false);
+       if (err)
+               goto err_out;
+
+       err = do_run_single_threaded(session, threads, &target, true);
+
+err_out:
+       if (threads)
+               perf_thread_map__put(threads);
+
+       perf_session__delete(session);
+       return err;
+}
+
+static int do_run_multi_threaded(struct target *target,
+                               unsigned int nr_threads_synthesize)
+{
+       struct timeval start, end, diff;
+       u64 runtime_us;
+       unsigned int i;
+       double time_average, time_stddev, event_average, event_stddev;
+       int err;
+       struct stats time_stats, event_stats;
+       struct perf_session *session;
+
+       init_stats(&time_stats);
+       init_stats(&event_stats);
+       for (i = 0; i < multi_iterations; i++) {
+               session = perf_session__new(NULL, false, NULL);
+               if (!session)
+                       return -ENOMEM;
+
+               atomic_set(&event_count, 0);
+               gettimeofday(&start, NULL);
+               err = __machine__synthesize_threads(&session->machines.host,
+                                               NULL,
+                                               target, NULL,
+                                               process_synthesized_event,
+                                               false,
+                                               nr_threads_synthesize);
+               if (err) {
+                       perf_session__delete(session);
+                       return err;
+               }
+
+               gettimeofday(&end, NULL);
+               timersub(&end, &start, &diff);
+               runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+               update_stats(&time_stats, runtime_us);
+               update_stats(&event_stats, atomic_read(&event_count));
+               perf_session__delete(session);
+       }
+
+       time_average = avg_stats(&time_stats);
+       time_stddev = stddev_stats(&time_stats);
+       printf("    Average synthesis took: %.3f usec (+- %.3f usec)\n",
+               time_average, time_stddev);
+
+       event_average = avg_stats(&event_stats);
+       event_stddev = stddev_stats(&event_stats);
+       printf("    Average num. events: %.3f (+- %.3f)\n",
+               event_average, event_stddev);
+
+       printf("    Average time per event %.3f usec\n",
+               time_average / event_average);
+       return 0;
+}
+
+static int run_multi_threaded(void)
+{
+       struct target target = {
+               .cpu_list = "0"
+       };
+       unsigned int nr_threads_synthesize;
+       int err;
+
+       if (max_threads == UINT_MAX)
+               max_threads = sysconf(_SC_NPROCESSORS_ONLN);
+
+       puts(
+"Computing performance of multi threaded perf event synthesis by\n"
+"synthesizing events on CPU 0:");
+
+       for (nr_threads_synthesize = min_threads;
+            nr_threads_synthesize <= max_threads;
+            nr_threads_synthesize++) {
+               if (nr_threads_synthesize == 1)
+                       perf_set_singlethreaded();
+               else
+                       perf_set_multithreaded();
+
+               printf("  Number of synthesis threads: %u\n",
+                       nr_threads_synthesize);
+
+               err = do_run_multi_threaded(&target, nr_threads_synthesize);
+               if (err)
+                       return err;
+       }
+       perf_set_singlethreaded();
+       return 0;
+}
+
+int bench_synthesize(int argc, const char **argv)
+{
+       int err = 0;
+
+       argc = parse_options(argc, argv, options, bench_usage, 0);
+       if (argc) {
+               usage_with_options(bench_usage, options);
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * If neither single threaded or multi-threaded are specified, default
+        * to running just single threaded.
+        */
+       if (!run_st && !run_mt)
+               run_st = true;
+
+       if (run_st)
+               err = run_single_threaded();
+
+       if (!err && run_mt)
+               err = run_multi_threaded();
+
+       return err;
+}
index 6c0a0412502ebb5e731156f47fcf87ccb2b9b55b..d3e5a84f87a2dc98f095239e43d8115acb5f692d 100644 (file)
@@ -212,11 +212,9 @@ static bool has_annotation(struct perf_annotate *ann)
        return ui__has_annotation() || ann->use_stdio2;
 }
 
-static int perf_evsel__add_sample(struct evsel *evsel,
-                                 struct perf_sample *sample,
-                                 struct addr_location *al,
-                                 struct perf_annotate *ann,
-                                 struct machine *machine)
+static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample,
+                            struct addr_location *al, struct perf_annotate *ann,
+                            struct machine *machine)
 {
        struct hists *hists = evsel__hists(evsel);
        struct hist_entry *he;
@@ -278,7 +276,7 @@ static int process_sample_event(struct perf_tool *tool,
                goto out_put;
 
        if (!al.filtered &&
-           perf_evsel__add_sample(evsel, sample, &al, ann, machine)) {
+           evsel__add_sample(evsel, sample, &al, ann, machine)) {
                pr_warning("problem incrementing symbol count, "
                           "skipping event\n");
                ret = -1;
@@ -433,11 +431,10 @@ static int __cmd_annotate(struct perf_annotate *ann)
                        total_nr_samples += nr_samples;
                        hists__collapse_resort(hists, NULL);
                        /* Don't sort callchain */
-                       perf_evsel__reset_sample_bit(pos, CALLCHAIN);
+                       evsel__reset_sample_bit(pos, CALLCHAIN);
                        perf_evsel__output_resort(pos, NULL);
 
-                       if (symbol_conf.event_group &&
-                           !perf_evsel__is_group_leader(pos))
+                       if (symbol_conf.event_group && !evsel__is_group_leader(pos))
                                continue;
 
                        hists__find_annotations(hists, pos, ann);
index c06fe21c86134423fc1da3dd6bd4091a0f9cd2e5..083273209c885cb30fa1ff1a556331f4d8b400bb 100644 (file)
@@ -76,6 +76,12 @@ static struct bench epoll_benchmarks[] = {
 };
 #endif // HAVE_EVENTFD
 
+static struct bench internals_benchmarks[] = {
+       { "synthesize", "Benchmark perf event synthesis",       bench_synthesize        },
+       { "kallsyms-parse", "Benchmark kallsyms parsing",       bench_kallsyms_parse    },
+       { NULL,         NULL,                                   NULL                    }
+};
+
 struct collection {
        const char      *name;
        const char      *summary;
@@ -92,6 +98,7 @@ static struct collection collections[] = {
 #ifdef HAVE_EVENTFD
        {"epoll",       "Epoll stressing benchmarks",                   epoll_benchmarks        },
 #endif
+       { "internals",  "Perf-internals benchmarks",                    internals_benchmarks    },
        { "all",        "All benchmarks",                               NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };
index 246ac0b4d54fbfe851de11d4e412307bd915b9ca..1baf4cae086fe7237320bb7c7cfe3e2554319a2e 100644 (file)
@@ -95,6 +95,7 @@ struct perf_c2c {
        bool                     use_stdio;
        bool                     stats_only;
        bool                     symbol_full;
+       bool                     stitch_lbr;
 
        /* HITM shared clines stats */
        struct c2c_stats        hitm_stats;
@@ -273,6 +274,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
                return -1;
        }
 
+       if (c2c.stitch_lbr)
+               al.thread->lbr_stitch_enable = true;
+
        ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
                                        evsel, &al, sysctl_perf_event_max_stack);
        if (ret)
@@ -1705,7 +1709,7 @@ static struct c2c_dimension *get_dimension(const char *name)
 
                if (!strcmp(dim->name, name))
                        return dim;
-       };
+       }
 
        return NULL;
 }
@@ -1921,7 +1925,7 @@ static bool he__display(struct hist_entry *he, struct c2c_stats *stats)
                FILTER_HITM(tot_hitm);
        default:
                break;
-       };
+       }
 
 #undef FILTER_HITM
 
@@ -2255,8 +2259,7 @@ static void print_c2c_info(FILE *out, struct perf_session *session)
        fprintf(out, "=================================================\n");
 
        evlist__for_each_entry(evlist, evsel) {
-               fprintf(out, "%-36s: %s\n", first ? "  Events" : "",
-                       perf_evsel__name(evsel));
+               fprintf(out, "%-36s: %s\n", first ? "  Events" : "", evsel__name(evsel));
                first = false;
        }
        fprintf(out, "  Cachelines sort on                : %s HITMs\n",
@@ -2601,6 +2604,12 @@ static int setup_callchain(struct evlist *evlist)
                }
        }
 
+       if (c2c.stitch_lbr && (mode != CALLCHAIN_LBR)) {
+               ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+                           "Please apply --call-graph lbr when recording.\n");
+               c2c.stitch_lbr = false;
+       }
+
        callchain_param.record_mode = mode;
        callchain_param.min_percent = 0;
        return 0;
@@ -2752,6 +2761,8 @@ static int perf_c2c__report(int argc, const char **argv)
        OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
                   "coalesce fields: pid,tid,iaddr,dso"),
        OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
+       OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
+                   "Enable LBR callgraph stitching approach"),
        OPT_PARENT(c2c_options),
        OPT_END()
        };
@@ -2947,7 +2958,7 @@ static int perf_c2c__record(int argc, const char **argv)
 
                rec_argv[i++] = "-e";
                rec_argv[i++] = perf_mem_events__name(j);
-       };
+       }
 
        if (all_user)
                rec_argv[i++] = "--all-user";
index c94a002f295e007f05426d2edd6520cc3d3a4a37..f8c9bdd8269afdb2f661fcd21401deb131b60cdc 100644 (file)
@@ -467,7 +467,7 @@ static struct evsel *evsel_match(struct evsel *evsel,
        struct evsel *e;
 
        evlist__for_each_entry(evlist, e) {
-               if (perf_evsel__match2(evsel, e))
+               if (evsel__match2(evsel, e))
                        return e;
        }
 
@@ -981,7 +981,7 @@ static void data_process(void)
 
                if (!quiet) {
                        fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
-                               perf_evsel__name(evsel_base));
+                               evsel__name(evsel_base));
                }
 
                first = false;
@@ -990,7 +990,7 @@ static void data_process(void)
                        data__fprintf();
 
                /* Don't sort callchain for perf diff */
-               perf_evsel__reset_sample_bit(evsel_base, CALLCHAIN);
+               evsel__reset_sample_bit(evsel_base, CALLCHAIN);
 
                hists__process(hists_base);
        }
@@ -1562,7 +1562,7 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
 
        default:
                BUG_ON(1);
-       };
+       }
 }
 
 static void
index d5adc417a4ca4a77839de5ca7528f29944ecbef2..55eda54240fbf872d194c0924f3b87a6f3ecce56 100644 (file)
@@ -284,10 +284,11 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
                .events = POLLIN,
        };
 
-       if (!perf_cap__capable(CAP_SYS_ADMIN)) {
+       if (!(perf_cap__capable(CAP_PERFMON) ||
+             perf_cap__capable(CAP_SYS_ADMIN))) {
                pr_err("ftrace only works for %s!\n",
 #ifdef HAVE_LIBCAP_SUPPORT
-               "users with the SYS_ADMIN capability"
+               "users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
 #else
                "root"
 #endif
index 7e124a7b8bfdc4e42ba48846caeab4f66a46c41e..53932db97a79d015d63ac68b2cd7de5f93a6398d 100644 (file)
@@ -536,7 +536,7 @@ static int perf_inject__sched_stat(struct perf_tool *tool,
        union perf_event *event_sw;
        struct perf_sample sample_sw;
        struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
-       u32 pid = perf_evsel__intval(evsel, sample, "pid");
+       u32 pid = evsel__intval(evsel, sample, "pid");
 
        list_for_each_entry(ent, &inject->samples, node) {
                if (pid == ent->tid)
@@ -546,7 +546,7 @@ static int perf_inject__sched_stat(struct perf_tool *tool,
        return 0;
 found:
        event_sw = &ent->event[0];
-       perf_evsel__parse_sample(evsel, event_sw, &sample_sw);
+       evsel__parse_sample(evsel, event_sw, &sample_sw);
 
        sample_sw.period = sample->period;
        sample_sw.time   = sample->time;
@@ -561,11 +561,10 @@ static void sig_handler(int sig __maybe_unused)
        session_done = 1;
 }
 
-static int perf_evsel__check_stype(struct evsel *evsel,
-                                  u64 sample_type, const char *sample_msg)
+static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char *sample_msg)
 {
        struct perf_event_attr *attr = &evsel->core.attr;
-       const char *name = perf_evsel__name(evsel);
+       const char *name = evsel__name(evsel);
 
        if (!(attr->sample_type & sample_type)) {
                pr_err("Samples for %s event do not have %s attribute set.",
@@ -622,10 +621,10 @@ static int __cmd_inject(struct perf_inject *inject)
                struct evsel *evsel;
 
                evlist__for_each_entry(session->evlist, evsel) {
-                       const char *name = perf_evsel__name(evsel);
+                       const char *name = evsel__name(evsel);
 
                        if (!strcmp(name, "sched:sched_switch")) {
-                               if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
+                               if (evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID"))
                                        return -EINVAL;
 
                                evsel->handler = perf_inject__sched_switch;
@@ -684,14 +683,14 @@ static int __cmd_inject(struct perf_inject *inject)
 
                        perf_header__clear_feat(&session->header,
                                                HEADER_AUXTRACE);
-                       if (inject->itrace_synth_opts.last_branch)
+                       if (inject->itrace_synth_opts.last_branch ||
+                           inject->itrace_synth_opts.add_last_branch)
                                perf_header__set_feat(&session->header,
                                                      HEADER_BRANCH_STACK);
                        evsel = perf_evlist__id2evsel_strict(session->evlist,
                                                             inject->aux_id);
                        if (evsel) {
-                               pr_debug("Deleting %s\n",
-                                        perf_evsel__name(evsel));
+                               pr_debug("Deleting %s\n", evsel__name(evsel));
                                evlist__remove(session->evlist, evsel);
                                evsel__delete(evsel);
                        }
index 003c85f5f56c9bc48e1bbae1c48d195dcbfd68ef..38a5ab683ebcec7cc72c05c0a7352e611d37ef73 100644 (file)
@@ -169,13 +169,12 @@ static int insert_caller_stat(unsigned long call_site,
        return 0;
 }
 
-static int perf_evsel__process_alloc_event(struct evsel *evsel,
-                                          struct perf_sample *sample)
+static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *sample)
 {
-       unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
-                     call_site = perf_evsel__intval(evsel, sample, "call_site");
-       int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
-           bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
+       unsigned long ptr = evsel__intval(evsel, sample, "ptr"),
+                     call_site = evsel__intval(evsel, sample, "call_site");
+       int bytes_req = evsel__intval(evsel, sample, "bytes_req"),
+           bytes_alloc = evsel__intval(evsel, sample, "bytes_alloc");
 
        if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
            insert_caller_stat(call_site, bytes_req, bytes_alloc))
@@ -188,14 +187,13 @@ static int perf_evsel__process_alloc_event(struct evsel *evsel,
        return 0;
 }
 
-static int perf_evsel__process_alloc_node_event(struct evsel *evsel,
-                                               struct perf_sample *sample)
+static int evsel__process_alloc_node_event(struct evsel *evsel, struct perf_sample *sample)
 {
-       int ret = perf_evsel__process_alloc_event(evsel, sample);
+       int ret = evsel__process_alloc_event(evsel, sample);
 
        if (!ret) {
                int node1 = cpu__get_node(sample->cpu),
-                   node2 = perf_evsel__intval(evsel, sample, "node");
+                   node2 = evsel__intval(evsel, sample, "node");
 
                if (node1 != node2)
                        nr_cross_allocs++;
@@ -232,10 +230,9 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr,
        return NULL;
 }
 
-static int perf_evsel__process_free_event(struct evsel *evsel,
-                                         struct perf_sample *sample)
+static int evsel__process_free_event(struct evsel *evsel, struct perf_sample *sample)
 {
-       unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
+       unsigned long ptr = evsel__intval(evsel, sample, "ptr");
        struct alloc_stat *s_alloc, *s_caller;
 
        s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
@@ -784,13 +781,12 @@ static int parse_gfp_flags(struct evsel *evsel, struct perf_sample *sample,
        return 0;
 }
 
-static int perf_evsel__process_page_alloc_event(struct evsel *evsel,
-                                               struct perf_sample *sample)
+static int evsel__process_page_alloc_event(struct evsel *evsel, struct perf_sample *sample)
 {
        u64 page;
-       unsigned int order = perf_evsel__intval(evsel, sample, "order");
-       unsigned int gfp_flags = perf_evsel__intval(evsel, sample, "gfp_flags");
-       unsigned int migrate_type = perf_evsel__intval(evsel, sample,
+       unsigned int order = evsel__intval(evsel, sample, "order");
+       unsigned int gfp_flags = evsel__intval(evsel, sample, "gfp_flags");
+       unsigned int migrate_type = evsel__intval(evsel, sample,
                                                       "migratetype");
        u64 bytes = kmem_page_size << order;
        u64 callsite;
@@ -802,9 +798,9 @@ static int perf_evsel__process_page_alloc_event(struct evsel *evsel,
        };
 
        if (use_pfn)
-               page = perf_evsel__intval(evsel, sample, "pfn");
+               page = evsel__intval(evsel, sample, "pfn");
        else
-               page = perf_evsel__intval(evsel, sample, "page");
+               page = evsel__intval(evsel, sample, "page");
 
        nr_page_allocs++;
        total_page_alloc_bytes += bytes;
@@ -857,11 +853,10 @@ static int perf_evsel__process_page_alloc_event(struct evsel *evsel,
        return 0;
 }
 
-static int perf_evsel__process_page_free_event(struct evsel *evsel,
-                                               struct perf_sample *sample)
+static int evsel__process_page_free_event(struct evsel *evsel, struct perf_sample *sample)
 {
        u64 page;
-       unsigned int order = perf_evsel__intval(evsel, sample, "order");
+       unsigned int order = evsel__intval(evsel, sample, "order");
        u64 bytes = kmem_page_size << order;
        struct page_stat *pstat;
        struct page_stat this = {
@@ -869,9 +864,9 @@ static int perf_evsel__process_page_free_event(struct evsel *evsel,
        };
 
        if (use_pfn)
-               page = perf_evsel__intval(evsel, sample, "pfn");
+               page = evsel__intval(evsel, sample, "pfn");
        else
-               page = perf_evsel__intval(evsel, sample, "page");
+               page = evsel__intval(evsel, sample, "page");
 
        nr_page_frees++;
        total_page_free_bytes += bytes;
@@ -1371,15 +1366,15 @@ static int __cmd_kmem(struct perf_session *session)
        struct evsel *evsel;
        const struct evsel_str_handler kmem_tracepoints[] = {
                /* slab allocator */
-               { "kmem:kmalloc",               perf_evsel__process_alloc_event, },
-               { "kmem:kmem_cache_alloc",      perf_evsel__process_alloc_event, },
-               { "kmem:kmalloc_node",          perf_evsel__process_alloc_node_event, },
-               { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
-               { "kmem:kfree",                 perf_evsel__process_free_event, },
-               { "kmem:kmem_cache_free",       perf_evsel__process_free_event, },
+               { "kmem:kmalloc",               evsel__process_alloc_event, },
+               { "kmem:kmem_cache_alloc",      evsel__process_alloc_event, },
+               { "kmem:kmalloc_node",          evsel__process_alloc_node_event, },
+               { "kmem:kmem_cache_alloc_node", evsel__process_alloc_node_event, },
+               { "kmem:kfree",                 evsel__process_free_event, },
+               { "kmem:kmem_cache_free",       evsel__process_free_event, },
                /* page allocator */
-               { "kmem:mm_page_alloc",         perf_evsel__process_page_alloc_event, },
-               { "kmem:mm_page_free",          perf_evsel__process_page_free_event, },
+               { "kmem:mm_page_alloc",         evsel__process_page_alloc_event, },
+               { "kmem:mm_page_free",          evsel__process_page_free_event, },
        };
 
        if (!perf_session__has_traces(session, "kmem record"))
@@ -1391,8 +1386,8 @@ static int __cmd_kmem(struct perf_session *session)
        }
 
        evlist__for_each_entry(session->evlist, evsel) {
-               if (!strcmp(perf_evsel__name(evsel), "kmem:mm_page_alloc") &&
-                   perf_evsel__field(evsel, "pfn")) {
+               if (!strcmp(evsel__name(evsel), "kmem:mm_page_alloc") &&
+                   evsel__field(evsel, "pfn")) {
                        use_pfn = true;
                        break;
                }
index 577af4f3297aaa6cd03181f34bb3fac962e0d564..95a77058023e66c0a3ce80cf1f42f8cbfd706147 100644 (file)
@@ -69,7 +69,7 @@ void exit_event_get_key(struct evsel *evsel,
                        struct event_key *key)
 {
        key->info = 0;
-       key->key = perf_evsel__intval(evsel, sample, kvm_exit_reason);
+       key->key  = evsel__intval(evsel, sample, kvm_exit_reason);
 }
 
 bool kvm_exit_event(struct evsel *evsel)
@@ -416,8 +416,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
                        return NULL;
                }
 
-               vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample,
-                                                         vcpu_id_str);
+               vcpu_record->vcpu_id = evsel__intval(evsel, sample, vcpu_id_str);
                thread__set_priv(thread, vcpu_record);
        }
 
@@ -1033,16 +1032,16 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
                struct perf_event_attr *attr = &pos->core.attr;
 
                /* make sure these *are* set */
-               perf_evsel__set_sample_bit(pos, TID);
-               perf_evsel__set_sample_bit(pos, TIME);
-               perf_evsel__set_sample_bit(pos, CPU);
-               perf_evsel__set_sample_bit(pos, RAW);
+               evsel__set_sample_bit(pos, TID);
+               evsel__set_sample_bit(pos, TIME);
+               evsel__set_sample_bit(pos, CPU);
+               evsel__set_sample_bit(pos, RAW);
                /* make sure these are *not*; want as small a sample as possible */
-               perf_evsel__reset_sample_bit(pos, PERIOD);
-               perf_evsel__reset_sample_bit(pos, IP);
-               perf_evsel__reset_sample_bit(pos, CALLCHAIN);
-               perf_evsel__reset_sample_bit(pos, ADDR);
-               perf_evsel__reset_sample_bit(pos, READ);
+               evsel__reset_sample_bit(pos, PERIOD);
+               evsel__reset_sample_bit(pos, IP);
+               evsel__reset_sample_bit(pos, CALLCHAIN);
+               evsel__reset_sample_bit(pos, ADDR);
+               evsel__reset_sample_bit(pos, READ);
                attr->mmap = 0;
                attr->comm = 0;
                attr->task = 0;
index 474dfd59d7eb23585b08075d817f51213fbf2f7b..f0a1dbacb46c718df23086fa57cafb680538bcd0 100644 (file)
@@ -48,7 +48,7 @@ struct lock_stat {
        struct rb_node          rb;             /* used for sorting */
 
        /*
-        * FIXME: perf_evsel__intval() returns u64,
+        * FIXME: evsel__intval() returns u64,
         * so address of lockdep_map should be dealed as 64bit.
         * Is there more better solution?
         */
@@ -404,9 +404,9 @@ static int report_lock_acquire_event(struct evsel *evsel,
        struct lock_stat *ls;
        struct thread_stat *ts;
        struct lock_seq_stat *seq;
-       const char *name = perf_evsel__strval(evsel, sample, "name");
-       u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
-       int flag = perf_evsel__intval(evsel, sample, "flag");
+       const char *name = evsel__strval(evsel, sample, "name");
+       u64 tmp  = evsel__intval(evsel, sample, "lockdep_addr");
+       int flag = evsel__intval(evsel, sample, "flag");
 
        memcpy(&addr, &tmp, sizeof(void *));
 
@@ -477,8 +477,8 @@ static int report_lock_acquired_event(struct evsel *evsel,
        struct thread_stat *ts;
        struct lock_seq_stat *seq;
        u64 contended_term;
-       const char *name = perf_evsel__strval(evsel, sample, "name");
-       u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+       const char *name = evsel__strval(evsel, sample, "name");
+       u64 tmp = evsel__intval(evsel, sample, "lockdep_addr");
 
        memcpy(&addr, &tmp, sizeof(void *));
 
@@ -539,8 +539,8 @@ static int report_lock_contended_event(struct evsel *evsel,
        struct lock_stat *ls;
        struct thread_stat *ts;
        struct lock_seq_stat *seq;
-       const char *name = perf_evsel__strval(evsel, sample, "name");
-       u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+       const char *name = evsel__strval(evsel, sample, "name");
+       u64 tmp = evsel__intval(evsel, sample, "lockdep_addr");
 
        memcpy(&addr, &tmp, sizeof(void *));
 
@@ -594,8 +594,8 @@ static int report_lock_release_event(struct evsel *evsel,
        struct lock_stat *ls;
        struct thread_stat *ts;
        struct lock_seq_stat *seq;
-       const char *name = perf_evsel__strval(evsel, sample, "name");
-       u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+       const char *name = evsel__strval(evsel, sample, "name");
+       u64 tmp = evsel__intval(evsel, sample, "lockdep_addr");
 
        memcpy(&addr, &tmp, sizeof(void *));
 
@@ -657,32 +657,28 @@ static struct trace_lock_handler report_lock_ops  = {
 
 static struct trace_lock_handler *trace_handler;
 
-static int perf_evsel__process_lock_acquire(struct evsel *evsel,
-                                            struct perf_sample *sample)
+static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample)
 {
        if (trace_handler->acquire_event)
                return trace_handler->acquire_event(evsel, sample);
        return 0;
 }
 
-static int perf_evsel__process_lock_acquired(struct evsel *evsel,
-                                             struct perf_sample *sample)
+static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample)
 {
        if (trace_handler->acquired_event)
                return trace_handler->acquired_event(evsel, sample);
        return 0;
 }
 
-static int perf_evsel__process_lock_contended(struct evsel *evsel,
-                                             struct perf_sample *sample)
+static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample)
 {
        if (trace_handler->contended_event)
                return trace_handler->contended_event(evsel, sample);
        return 0;
 }
 
-static int perf_evsel__process_lock_release(struct evsel *evsel,
-                                           struct perf_sample *sample)
+static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample)
 {
        if (trace_handler->release_event)
                return trace_handler->release_event(evsel, sample);
@@ -775,7 +771,7 @@ static void dump_threads(void)
                pr_info("%10d: %s\n", st->tid, thread__comm_str(t));
                node = rb_next(node);
                thread__put(t);
-       };
+       }
 }
 
 static void dump_map(void)
@@ -849,10 +845,10 @@ static void sort_result(void)
 }
 
 static const struct evsel_str_handler lock_tracepoints[] = {
-       { "lock:lock_acquire",   perf_evsel__process_lock_acquire,   }, /* CONFIG_LOCKDEP */
-       { "lock:lock_acquired",  perf_evsel__process_lock_acquired,  }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
-       { "lock:lock_contended", perf_evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
-       { "lock:lock_release",   perf_evsel__process_lock_release,   }, /* CONFIG_LOCKDEP */
+       { "lock:lock_acquire",   evsel__process_lock_acquire,   }, /* CONFIG_LOCKDEP */
+       { "lock:lock_acquired",  evsel__process_lock_acquired,  }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
+       { "lock:lock_contended", evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
+       { "lock:lock_release",   evsel__process_lock_release,   }, /* CONFIG_LOCKDEP */
 };
 
 static bool force;
index a13f5817d6fca4abac7cdfa80f5b90b1a8c3a097..68a7eb84561a1b12d94bd2196430c7d8fbfc04ff 100644 (file)
@@ -123,7 +123,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
 
                rec_argv[i++] = "-e";
                rec_argv[i++] = perf_mem_events__name(j);
-       };
+       }
 
        if (all_user)
                rec_argv[i++] = "--all-user";
index 1ab349abe9046949f57eaefd69e0d1f51910b937..e4efdbf1a81e939c4395f7bc161c6cbb37d6c7e2 100644 (file)
@@ -34,6 +34,7 @@
 #include "util/tsc.h"
 #include "util/parse-branch-options.h"
 #include "util/parse-regs-options.h"
+#include "util/perf_api_probe.h"
 #include "util/llvm-utils.h"
 #include "util/bpf-loader.h"
 #include "util/trigger.h"
@@ -43,6 +44,7 @@
 #include "util/time-utils.h"
 #include "util/units.h"
 #include "util/bpf-event.h"
+#include "util/util.h"
 #include "asm/bug.h"
 #include "perf.h"
 
@@ -50,6 +52,7 @@
 #include <inttypes.h>
 #include <locale.h>
 #include <poll.h>
+#include <pthread.h>
 #include <unistd.h>
 #include <sched.h>
 #include <signal.h>
@@ -84,7 +87,10 @@ struct record {
        struct auxtrace_record  *itr;
        struct evlist   *evlist;
        struct perf_session     *session;
+       struct evlist           *sb_evlist;
+       pthread_t               thread_id;
        int                     realtime_prio;
+       bool                    switch_output_event_set;
        bool                    no_buildid;
        bool                    no_buildid_set;
        bool                    no_buildid_cache;
@@ -503,6 +509,20 @@ static int process_synthesized_event(struct perf_tool *tool,
        return record__write(rec, NULL, event, event->header.size);
 }
 
+static int process_locked_synthesized_event(struct perf_tool *tool,
+                                    union perf_event *event,
+                                    struct perf_sample *sample __maybe_unused,
+                                    struct machine *machine __maybe_unused)
+{
+       static pthread_mutex_t synth_lock = PTHREAD_MUTEX_INITIALIZER;
+       int ret;
+
+       pthread_mutex_lock(&synth_lock);
+       ret = process_synthesized_event(tool, event, sample, machine);
+       pthread_mutex_unlock(&synth_lock);
+       return ret;
+}
+
 static int record__pushfn(struct mmap *map, void *to, void *bf, size_t size)
 {
        struct record *rec = to;
@@ -825,7 +845,7 @@ static int record__open(struct record *rec)
        evlist__for_each_entry(evlist, pos) {
 try_again:
                if (evsel__open(pos, pos->core.cpus, pos->core.threads) < 0) {
-                       if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
+                       if (evsel__fallback(pos, errno, msg, sizeof(msg))) {
                                if (verbose > 0)
                                        ui__warning("%s\n", msg);
                                goto try_again;
@@ -837,8 +857,7 @@ try_again:
                                goto try_again;
                        }
                        rc = -errno;
-                       perf_evsel__open_strerror(pos, &opts->target,
-                                                 errno, msg, sizeof(msg));
+                       evsel__open_strerror(pos, &opts->target, errno, msg, sizeof(msg));
                        ui__error("%s\n", msg);
                        goto out;
                }
@@ -859,7 +878,7 @@ try_again:
 
        if (perf_evlist__apply_filters(evlist, &pos)) {
                pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
-                       pos->filter, perf_evsel__name(pos), errno,
+                       pos->filter, evsel__name(pos), errno,
                        str_error_r(errno, msg, sizeof(msg)));
                rc = -1;
                goto out;
@@ -1288,6 +1307,7 @@ static int record__synthesize(struct record *rec, bool tail)
        struct perf_tool *tool = &rec->tool;
        int fd = perf_data__fd(data);
        int err = 0;
+       event_op f = process_synthesized_event;
 
        if (rec->opts.tail_synthesize != tail)
                return 0;
@@ -1402,13 +1422,67 @@ static int record__synthesize(struct record *rec, bool tail)
        if (err < 0)
                pr_warning("Couldn't synthesize cgroup events.\n");
 
+       if (rec->opts.nr_threads_synthesize > 1) {
+               perf_set_multithreaded();
+               f = process_locked_synthesized_event;
+       }
+
        err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->core.threads,
-                                           process_synthesized_event, opts->sample_address,
-                                           1);
+                                           f, opts->sample_address,
+                                           rec->opts.nr_threads_synthesize);
+
+       if (rec->opts.nr_threads_synthesize > 1)
+               perf_set_singlethreaded();
+
 out:
        return err;
 }
 
+static int record__process_signal_event(union perf_event *event __maybe_unused, void *data)
+{
+       struct record *rec = data;
+       pthread_kill(rec->thread_id, SIGUSR2);
+       return 0;
+}
+
+static int record__setup_sb_evlist(struct record *rec)
+{
+       struct record_opts *opts = &rec->opts;
+
+       if (rec->sb_evlist != NULL) {
+               /*
+                * We get here if --switch-output-event populated the
+                * sb_evlist, so associate a callback that will send a SIGUSR2
+                * to the main thread.
+                */
+               evlist__set_cb(rec->sb_evlist, record__process_signal_event, rec);
+               rec->thread_id = pthread_self();
+       }
+
+       if (!opts->no_bpf_event) {
+               if (rec->sb_evlist == NULL) {
+                       rec->sb_evlist = evlist__new();
+
+                       if (rec->sb_evlist == NULL) {
+                               pr_err("Couldn't create side band evlist.\n.");
+                               return -1;
+                       }
+               }
+
+               if (evlist__add_bpf_sb_event(rec->sb_evlist, &rec->session->header.env)) {
+                       pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n.");
+                       return -1;
+               }
+       }
+
+       if (perf_evlist__start_sb_thread(rec->sb_evlist, &rec->opts.target)) {
+               pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
+               opts->no_bpf_event = true;
+       }
+
+       return 0;
+}
+
 static int __cmd_record(struct record *rec, int argc, const char **argv)
 {
        int err;
@@ -1420,7 +1494,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
        struct perf_data *data = &rec->data;
        struct perf_session *session;
        bool disabled = false, draining = false;
-       struct evlist *sb_evlist = NULL;
        int fd;
        float ratio = 0;
 
@@ -1546,21 +1619,17 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                        goto out_child;
        }
 
+       err = -1;
        if (!rec->no_buildid
            && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) {
                pr_err("Couldn't generate buildids. "
                       "Use --no-buildid to profile anyway.\n");
-               err = -1;
                goto out_child;
        }
 
-       if (!opts->no_bpf_event)
-               bpf_event__add_sb_event(&sb_evlist, &session->header.env);
-
-       if (perf_evlist__start_sb_thread(sb_evlist, &rec->opts.target)) {
-               pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
-               opts->no_bpf_event = true;
-       }
+       err = record__setup_sb_evlist(rec);
+       if (err)
+               goto out_child;
 
        err = record__synthesize(rec, false);
        if (err < 0)
@@ -1831,7 +1900,7 @@ out_delete_session:
        perf_session__delete(session);
 
        if (!opts->no_bpf_event)
-               perf_evlist__stop_sb_thread(sb_evlist);
+               perf_evlist__stop_sb_thread(rec->sb_evlist);
        return status;
 }
 
@@ -2142,10 +2211,19 @@ static int switch_output_setup(struct record *rec)
        };
        unsigned long val;
 
+       /*
+        * If we're using --switch-output-events, then we imply its 
+        * --switch-output=signal, as we'll send a SIGUSR2 from the side band
+        *  thread to its parent.
+        */
+       if (rec->switch_output_event_set)
+               goto do_signal;
+
        if (!s->set)
                return 0;
 
        if (!strcmp(s->str, "signal")) {
+do_signal:
                s->signal = true;
                pr_debug("switch-output with SIGUSR2 signal\n");
                goto enabled;
@@ -2232,6 +2310,7 @@ static struct record record = {
                        .default_per_cpu = true,
                },
                .mmap_flush          = MMAP_FLUSH_DEFAULT,
+               .nr_threads_synthesize = 1,
        },
        .tool = {
                .sample         = process_sample_event,
@@ -2402,6 +2481,9 @@ static struct option __record_options[] = {
                          &record.switch_output.set, "signal or size[BKMG] or time[smhd]",
                          "Switch output when receiving SIGUSR2 (signal) or cross a size or time threshold",
                          "signal"),
+       OPT_CALLBACK_SET(0, "switch-output-event", &record.sb_evlist, &record.switch_output_event_set, "switch output event",
+                        "switch output event selector. use 'perf list' to list available events",
+                        parse_events_option_new_evlist),
        OPT_INTEGER(0, "switch-max-files", &record.switch_output.num_files,
                   "Limit number of switch output generated files"),
        OPT_BOOLEAN(0, "dry-run", &dry_run,
@@ -2421,6 +2503,9 @@ static struct option __record_options[] = {
 #endif
        OPT_CALLBACK(0, "max-size", &record.output_max_size,
                     "size", "Limit the maximum size of the output file", parse_output_max_size),
+       OPT_UINTEGER(0, "num-thread-synthesize",
+                    &record.opts.nr_threads_synthesize,
+                    "number of threads to run for event synthesis"),
        OPT_END()
 };
 
index 26d8fc27e427ef1a95d6dc84786b106712f769ca..ba63390246c282ed2535d6a2e49eb34a3e0cb430 100644 (file)
@@ -84,6 +84,7 @@ struct report {
        bool                    header_only;
        bool                    nonany_branch_mode;
        bool                    group_set;
+       bool                    stitch_lbr;
        int                     max_stack;
        struct perf_read_values show_threads_values;
        struct annotation_options annotation_opts;
@@ -267,6 +268,9 @@ static int process_sample_event(struct perf_tool *tool,
                return -1;
        }
 
+       if (rep->stitch_lbr)
+               al.thread->lbr_stitch_enable = true;
+
        if (symbol_conf.hide_unresolved && al.sym == NULL)
                goto out_put;
 
@@ -317,7 +321,7 @@ static int process_read_event(struct perf_tool *tool,
        struct report *rep = container_of(tool, struct report, tool);
 
        if (rep->show_threads) {
-               const char *name = perf_evsel__name(evsel);
+               const char *name = evsel__name(evsel);
                int err = perf_read_values_add_value(&rep->show_threads_values,
                                           event->read.pid, event->read.tid,
                                           evsel->idx,
@@ -339,12 +343,14 @@ static int report__setup_sample_type(struct report *rep)
        bool is_pipe = perf_data__is_pipe(session->data);
 
        if (session->itrace_synth_opts->callchain ||
+           session->itrace_synth_opts->add_callchain ||
            (!is_pipe &&
             perf_header__has_feat(&session->header, HEADER_AUXTRACE) &&
             !session->itrace_synth_opts->set))
                sample_type |= PERF_SAMPLE_CALLCHAIN;
 
-       if (session->itrace_synth_opts->last_branch)
+       if (session->itrace_synth_opts->last_branch ||
+           session->itrace_synth_opts->add_last_branch)
                sample_type |= PERF_SAMPLE_BRANCH_STACK;
 
        if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
@@ -407,6 +413,12 @@ static int report__setup_sample_type(struct report *rep)
                        callchain_param.record_mode = CALLCHAIN_FP;
        }
 
+       if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
+               ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+                           "Please apply --call-graph lbr when recording.\n");
+               rep->stitch_lbr = false;
+       }
+
        /* ??? handle more cases than just ANY? */
        if (!(perf_evlist__combined_branch_type(session->evlist) &
                                PERF_SAMPLE_BRANCH_ANY))
@@ -447,10 +459,10 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
                nr_events = hists->stats.total_non_filtered_period;
        }
 
-       if (perf_evsel__is_group_event(evsel)) {
+       if (evsel__is_group_event(evsel)) {
                struct evsel *pos;
 
-               perf_evsel__group_desc(evsel, buf, size);
+               evsel__group_desc(evsel, buf, size);
                evname = buf;
 
                for_each_group_member(pos, evsel) {
@@ -525,10 +537,9 @@ static int perf_evlist__tty_browse_hists(struct evlist *evlist,
 
        evlist__for_each_entry(evlist, pos) {
                struct hists *hists = evsel__hists(pos);
-               const char *evname = perf_evsel__name(pos);
+               const char *evname = evsel__name(pos);
 
-               if (symbol_conf.event_group &&
-                   !perf_evsel__is_group_leader(pos))
+               if (symbol_conf.event_group && !evsel__is_group_leader(pos))
                        continue;
 
                hists__fprintf_nr_sample_events(hists, rep, evname, stdout);
@@ -670,8 +681,7 @@ static int report__collapse_hists(struct report *rep)
                        break;
 
                /* Non-group events are considered as leader */
-               if (symbol_conf.event_group &&
-                   !perf_evsel__is_group_leader(pos)) {
+               if (symbol_conf.event_group && !evsel__is_group_leader(pos)) {
                        struct hists *leader_hists = evsel__hists(pos->leader);
 
                        hists__match(leader_hists, hists);
@@ -1257,6 +1267,8 @@ int cmd_report(int argc, const char **argv)
                        "Show full source file name path for source lines"),
        OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph,
                    "Show callgraph from reference event"),
+       OPT_BOOLEAN(0, "stitch-lbr", &report.stitch_lbr,
+                   "Enable LBR callgraph stitching approach"),
        OPT_INTEGER(0, "socket-filter", &report.socket_filter,
                    "only show processor socket that match with this filter"),
        OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
@@ -1332,7 +1344,7 @@ int cmd_report(int argc, const char **argv)
        if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
                callchain_param.order = ORDER_CALLER;
 
-       if (itrace_synth_opts.callchain &&
+       if ((itrace_synth_opts.callchain || itrace_synth_opts.add_callchain) &&
            (int)itrace_synth_opts.callchain_sz > report.max_stack)
                report.max_stack = itrace_synth_opts.callchain_sz;
 
@@ -1380,7 +1392,7 @@ repeat:
                goto error;
        }
 
-       if (itrace_synth_opts.last_branch)
+       if (itrace_synth_opts.last_branch || itrace_synth_opts.add_last_branch)
                has_br_stack = true;
 
        if (has_br_stack && branch_call_mode)
@@ -1400,7 +1412,7 @@ repeat:
        }
        if (branch_call_mode) {
                callchain_param.key = CCKEY_ADDRESS;
-               callchain_param.branch_callstack = 1;
+               callchain_param.branch_callstack = true;
                symbol_conf.use_callchain = true;
                callchain_register_param(&callchain_param);
                if (sort_order == NULL)
index 82fcc2c15fe469b1dc20cd6ed8c516c1d8d0858e..459e4229945e4d777cc0fa36030fa3ebcce0e47b 100644 (file)
@@ -811,8 +811,8 @@ replay_wakeup_event(struct perf_sched *sched,
                    struct evsel *evsel, struct perf_sample *sample,
                    struct machine *machine __maybe_unused)
 {
-       const char *comm = perf_evsel__strval(evsel, sample, "comm");
-       const u32 pid    = perf_evsel__intval(evsel, sample, "pid");
+       const char *comm = evsel__strval(evsel, sample, "comm");
+       const u32 pid    = evsel__intval(evsel, sample, "pid");
        struct task_desc *waker, *wakee;
 
        if (verbose > 0) {
@@ -833,11 +833,11 @@ static int replay_switch_event(struct perf_sched *sched,
                               struct perf_sample *sample,
                               struct machine *machine __maybe_unused)
 {
-       const char *prev_comm  = perf_evsel__strval(evsel, sample, "prev_comm"),
-                  *next_comm  = perf_evsel__strval(evsel, sample, "next_comm");
-       const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
-                 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
-       const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
+       const char *prev_comm  = evsel__strval(evsel, sample, "prev_comm"),
+                  *next_comm  = evsel__strval(evsel, sample, "next_comm");
+       const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
+                 next_pid = evsel__intval(evsel, sample, "next_pid");
+       const u64 prev_state = evsel__intval(evsel, sample, "prev_state");
        struct task_desc *prev, __maybe_unused *next;
        u64 timestamp0, timestamp = sample->time;
        int cpu = sample->cpu;
@@ -1106,9 +1106,9 @@ static int latency_switch_event(struct perf_sched *sched,
                                struct perf_sample *sample,
                                struct machine *machine)
 {
-       const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
-                 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
-       const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
+       const u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
+                 next_pid = evsel__intval(evsel, sample, "next_pid");
+       const u64 prev_state = evsel__intval(evsel, sample, "prev_state");
        struct work_atoms *out_events, *in_events;
        struct thread *sched_out, *sched_in;
        u64 timestamp0, timestamp = sample->time;
@@ -1176,8 +1176,8 @@ static int latency_runtime_event(struct perf_sched *sched,
                                 struct perf_sample *sample,
                                 struct machine *machine)
 {
-       const u32 pid      = perf_evsel__intval(evsel, sample, "pid");
-       const u64 runtime  = perf_evsel__intval(evsel, sample, "runtime");
+       const u32 pid      = evsel__intval(evsel, sample, "pid");
+       const u64 runtime  = evsel__intval(evsel, sample, "runtime");
        struct thread *thread = machine__findnew_thread(machine, -1, pid);
        struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
        u64 timestamp = sample->time;
@@ -1211,7 +1211,7 @@ static int latency_wakeup_event(struct perf_sched *sched,
                                struct perf_sample *sample,
                                struct machine *machine)
 {
-       const u32 pid     = perf_evsel__intval(evsel, sample, "pid");
+       const u32 pid     = evsel__intval(evsel, sample, "pid");
        struct work_atoms *atoms;
        struct work_atom *atom;
        struct thread *wakee;
@@ -1272,7 +1272,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
                                      struct perf_sample *sample,
                                      struct machine *machine)
 {
-       const u32 pid = perf_evsel__intval(evsel, sample, "pid");
+       const u32 pid = evsel__intval(evsel, sample, "pid");
        u64 timestamp = sample->time;
        struct work_atoms *atoms;
        struct work_atom *atom;
@@ -1526,7 +1526,7 @@ map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid
 static int map_switch_event(struct perf_sched *sched, struct evsel *evsel,
                            struct perf_sample *sample, struct machine *machine)
 {
-       const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+       const u32 next_pid = evsel__intval(evsel, sample, "next_pid");
        struct thread *sched_in;
        struct thread_runtime *tr;
        int new_shortname;
@@ -1670,8 +1670,8 @@ static int process_sched_switch_event(struct perf_tool *tool,
 {
        struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
        int this_cpu = sample->cpu, err = 0;
-       u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
-           next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+       u32 prev_pid = evsel__intval(evsel, sample, "prev_pid"),
+           next_pid = evsel__intval(evsel, sample, "next_pid");
 
        if (sched->curr_pid[this_cpu] != (u32)-1) {
                /*
@@ -1848,7 +1848,7 @@ static inline void print_sched_time(unsigned long long nsecs, int width)
  * returns runtime data for event, allocating memory for it the
  * first time it is used.
  */
-static struct evsel_runtime *perf_evsel__get_runtime(struct evsel *evsel)
+static struct evsel_runtime *evsel__get_runtime(struct evsel *evsel)
 {
        struct evsel_runtime *r = evsel->priv;
 
@@ -1863,10 +1863,9 @@ static struct evsel_runtime *perf_evsel__get_runtime(struct evsel *evsel)
 /*
  * save last time event was seen per cpu
  */
-static void perf_evsel__save_time(struct evsel *evsel,
-                                 u64 timestamp, u32 cpu)
+static void evsel__save_time(struct evsel *evsel, u64 timestamp, u32 cpu)
 {
-       struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
+       struct evsel_runtime *r = evsel__get_runtime(evsel);
 
        if (r == NULL)
                return;
@@ -1890,9 +1889,9 @@ static void perf_evsel__save_time(struct evsel *evsel,
 }
 
 /* returns last time this event was seen on the given cpu */
-static u64 perf_evsel__get_time(struct evsel *evsel, u32 cpu)
+static u64 evsel__get_time(struct evsel *evsel, u32 cpu)
 {
-       struct evsel_runtime *r = perf_evsel__get_runtime(evsel);
+       struct evsel_runtime *r = evsel__get_runtime(evsel);
 
        if ((r == NULL) || (r->last_time == NULL) || (cpu >= r->ncpu))
                return 0;
@@ -2004,8 +2003,8 @@ static void timehist_print_sample(struct perf_sched *sched,
                                  u64 t, int state)
 {
        struct thread_runtime *tr = thread__priv(thread);
-       const char *next_comm = perf_evsel__strval(evsel, sample, "next_comm");
-       const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+       const char *next_comm = evsel__strval(evsel, sample, "next_comm");
+       const u32 next_pid = evsel__intval(evsel, sample, "next_pid");
        u32 max_cpus = sched->max_cpu + 1;
        char tstr[64];
        char nstr[30];
@@ -2136,8 +2135,8 @@ static bool is_idle_sample(struct perf_sample *sample,
                           struct evsel *evsel)
 {
        /* pid 0 == swapper == idle task */
-       if (strcmp(perf_evsel__name(evsel), "sched:sched_switch") == 0)
-               return perf_evsel__intval(evsel, sample, "prev_pid") == 0;
+       if (strcmp(evsel__name(evsel), "sched:sched_switch") == 0)
+               return evsel__intval(evsel, sample, "prev_pid") == 0;
 
        return sample->pid == 0;
 }
@@ -2334,7 +2333,7 @@ static struct thread *timehist_get_thread(struct perf_sched *sched,
                        itr->last_thread = thread;
 
                        /* copy task callchain when entering to idle */
-                       if (perf_evsel__intval(evsel, sample, "next_pid") == 0)
+                       if (evsel__intval(evsel, sample, "next_pid") == 0)
                                save_idle_callchain(sched, itr, sample);
                }
        }
@@ -2355,10 +2354,10 @@ static bool timehist_skip_sample(struct perf_sched *sched,
        }
 
        if (sched->idle_hist) {
-               if (strcmp(perf_evsel__name(evsel), "sched:sched_switch"))
+               if (strcmp(evsel__name(evsel), "sched:sched_switch"))
                        rc = true;
-               else if (perf_evsel__intval(evsel, sample, "prev_pid") != 0 &&
-                        perf_evsel__intval(evsel, sample, "next_pid") != 0)
+               else if (evsel__intval(evsel, sample, "prev_pid") != 0 &&
+                        evsel__intval(evsel, sample, "next_pid") != 0)
                        rc = true;
        }
 
@@ -2409,7 +2408,7 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool,
        struct thread *thread;
        struct thread_runtime *tr = NULL;
        /* want pid of awakened task not pid in sample */
-       const u32 pid = perf_evsel__intval(evsel, sample, "pid");
+       const u32 pid = evsel__intval(evsel, sample, "pid");
 
        thread = machine__findnew_thread(machine, 0, pid);
        if (thread == NULL)
@@ -2445,8 +2444,8 @@ static void timehist_print_migration_event(struct perf_sched *sched,
                return;
 
        max_cpus = sched->max_cpu + 1;
-       ocpu = perf_evsel__intval(evsel, sample, "orig_cpu");
-       dcpu = perf_evsel__intval(evsel, sample, "dest_cpu");
+       ocpu = evsel__intval(evsel, sample, "orig_cpu");
+       dcpu = evsel__intval(evsel, sample, "dest_cpu");
 
        thread = machine__findnew_thread(machine, sample->pid, sample->tid);
        if (thread == NULL)
@@ -2493,7 +2492,7 @@ static int timehist_migrate_task_event(struct perf_tool *tool,
        struct thread *thread;
        struct thread_runtime *tr = NULL;
        /* want pid of migrated task not pid in sample */
-       const u32 pid = perf_evsel__intval(evsel, sample, "pid");
+       const u32 pid = evsel__intval(evsel, sample, "pid");
 
        thread = machine__findnew_thread(machine, 0, pid);
        if (thread == NULL)
@@ -2524,8 +2523,7 @@ static int timehist_sched_change_event(struct perf_tool *tool,
        struct thread_runtime *tr = NULL;
        u64 tprev, t = sample->time;
        int rc = 0;
-       int state = perf_evsel__intval(evsel, sample, "prev_state");
-
+       int state = evsel__intval(evsel, sample, "prev_state");
 
        if (machine__resolve(machine, &al, sample) < 0) {
                pr_err("problem processing %d event. skipping it\n",
@@ -2549,7 +2547,7 @@ static int timehist_sched_change_event(struct perf_tool *tool,
                goto out;
        }
 
-       tprev = perf_evsel__get_time(evsel, sample->cpu);
+       tprev = evsel__get_time(evsel, sample->cpu);
 
        /*
         * If start time given:
@@ -2632,7 +2630,7 @@ out:
                tr->ready_to_run = 0;
        }
 
-       perf_evsel__save_time(evsel, sample->time, sample->cpu);
+       evsel__save_time(evsel, sample->time, sample->cpu);
 
        return rc;
 }
@@ -2942,7 +2940,7 @@ static int timehist_check_attr(struct perf_sched *sched,
        struct evsel_runtime *er;
 
        list_for_each_entry(evsel, &evlist->core.entries, core.node) {
-               er = perf_evsel__get_runtime(evsel);
+               er = evsel__get_runtime(evsel);
                if (er == NULL) {
                        pr_err("Failed to allocate memory for evsel runtime data\n");
                        return -1;
index 1f57a7ecdf3d0aee1eac1f5414f6d4657bc7bfc3..56d7bcd126714bc1d6251b5f693fe338809da0a3 100644 (file)
@@ -273,7 +273,7 @@ static struct evsel_script *perf_evsel_script__new(struct evsel *evsel,
        struct evsel_script *es = zalloc(sizeof(*es));
 
        if (es != NULL) {
-               if (asprintf(&es->filename, "%s.%s.dump", data->file.path, perf_evsel__name(evsel)) < 0)
+               if (asprintf(&es->filename, "%s.%s.dump", data->file.path, evsel__name(evsel)) < 0)
                        goto out_free;
                es->fp = fopen(es->filename, "w");
                if (es->fp == NULL)
@@ -351,10 +351,8 @@ static const char *output_field2str(enum perf_output_field field)
 
 #define PRINT_FIELD(x)  (output[output_type(attr->type)].fields & PERF_OUTPUT_##x)
 
-static int perf_evsel__do_check_stype(struct evsel *evsel,
-                                     u64 sample_type, const char *sample_msg,
-                                     enum perf_output_field field,
-                                     bool allow_user_set)
+static int evsel__do_check_stype(struct evsel *evsel, u64 sample_type, const char *sample_msg,
+                                enum perf_output_field field, bool allow_user_set)
 {
        struct perf_event_attr *attr = &evsel->core.attr;
        int type = output_type(attr->type);
@@ -366,7 +364,7 @@ static int perf_evsel__do_check_stype(struct evsel *evsel,
        if (output[type].user_set_fields & field) {
                if (allow_user_set)
                        return 0;
-               evname = perf_evsel__name(evsel);
+               evname = evsel__name(evsel);
                pr_err("Samples for '%s' event do not have %s attribute set. "
                       "Cannot print '%s' field.\n",
                       evname, sample_msg, output_field2str(field));
@@ -375,7 +373,7 @@ static int perf_evsel__do_check_stype(struct evsel *evsel,
 
        /* user did not ask for it explicitly so remove from the default list */
        output[type].fields &= ~field;
-       evname = perf_evsel__name(evsel);
+       evname = evsel__name(evsel);
        pr_debug("Samples for '%s' event do not have %s attribute set. "
                 "Skipping '%s' field.\n",
                 evname, sample_msg, output_field2str(field));
@@ -383,16 +381,13 @@ static int perf_evsel__do_check_stype(struct evsel *evsel,
        return 0;
 }
 
-static int perf_evsel__check_stype(struct evsel *evsel,
-                                  u64 sample_type, const char *sample_msg,
-                                  enum perf_output_field field)
+static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char *sample_msg,
+                             enum perf_output_field field)
 {
-       return perf_evsel__do_check_stype(evsel, sample_type, sample_msg, field,
-                                         false);
+       return evsel__do_check_stype(evsel, sample_type, sample_msg, field, false);
 }
 
-static int perf_evsel__check_attr(struct evsel *evsel,
-                                 struct perf_session *session)
+static int perf_evsel__check_attr(struct evsel *evsel, struct perf_session *session)
 {
        struct perf_event_attr *attr = &evsel->core.attr;
        bool allow_user_set;
@@ -404,32 +399,28 @@ static int perf_evsel__check_attr(struct evsel *evsel,
                                               HEADER_AUXTRACE);
 
        if (PRINT_FIELD(TRACE) &&
-               !perf_session__has_traces(session, "record -R"))
+           !perf_session__has_traces(session, "record -R"))
                return -EINVAL;
 
        if (PRINT_FIELD(IP)) {
-               if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP",
-                                           PERF_OUTPUT_IP))
+               if (evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", PERF_OUTPUT_IP))
                        return -EINVAL;
        }
 
        if (PRINT_FIELD(ADDR) &&
-               perf_evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
-                                          PERF_OUTPUT_ADDR, allow_user_set))
+           evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", PERF_OUTPUT_ADDR, allow_user_set))
                return -EINVAL;
 
        if (PRINT_FIELD(DATA_SRC) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC",
-                                       PERF_OUTPUT_DATA_SRC))
+           evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC", PERF_OUTPUT_DATA_SRC))
                return -EINVAL;
 
        if (PRINT_FIELD(WEIGHT) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT",
-                                       PERF_OUTPUT_WEIGHT))
+           evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT", PERF_OUTPUT_WEIGHT))
                return -EINVAL;
 
        if (PRINT_FIELD(SYM) &&
-               !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
+           !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
                pr_err("Display of symbols requested but neither sample IP nor "
                           "sample address\navailable. Hence, no addresses to convert "
                       "to symbols.\n");
@@ -441,7 +432,7 @@ static int perf_evsel__check_attr(struct evsel *evsel,
                return -EINVAL;
        }
        if (PRINT_FIELD(DSO) &&
-               !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
+           !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) {
                pr_err("Display of DSO requested but no address to convert.\n");
                return -EINVAL;
        }
@@ -458,33 +449,27 @@ static int perf_evsel__check_attr(struct evsel *evsel,
                return -EINVAL;
        }
        if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID",
-                                       PERF_OUTPUT_TID|PERF_OUTPUT_PID))
+           evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", PERF_OUTPUT_TID|PERF_OUTPUT_PID))
                return -EINVAL;
 
        if (PRINT_FIELD(TIME) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME",
-                                       PERF_OUTPUT_TIME))
+           evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME", PERF_OUTPUT_TIME))
                return -EINVAL;
 
        if (PRINT_FIELD(CPU) &&
-               perf_evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
-                                          PERF_OUTPUT_CPU, allow_user_set))
+           evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU", PERF_OUTPUT_CPU, allow_user_set))
                return -EINVAL;
 
        if (PRINT_FIELD(IREGS) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_REGS_INTR, "IREGS",
-                                       PERF_OUTPUT_IREGS))
+           evsel__check_stype(evsel, PERF_SAMPLE_REGS_INTR, "IREGS", PERF_OUTPUT_IREGS))
                return -EINVAL;
 
        if (PRINT_FIELD(UREGS) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_REGS_USER, "UREGS",
-                                       PERF_OUTPUT_UREGS))
+           evsel__check_stype(evsel, PERF_SAMPLE_REGS_USER, "UREGS", PERF_OUTPUT_UREGS))
                return -EINVAL;
 
        if (PRINT_FIELD(PHYS_ADDR) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR",
-                                       PERF_OUTPUT_PHYS_ADDR))
+           evsel__check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", PERF_OUTPUT_PHYS_ADDR))
                return -EINVAL;
 
        return 0;
@@ -604,8 +589,6 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
                printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r), val);
        }
 
-       fprintf(fp, "\n");
-
        return printed;
 }
 
@@ -1697,6 +1680,7 @@ struct perf_script {
        bool                    show_cgroup_events;
        bool                    allocated;
        bool                    per_event_dump;
+       bool                    stitch_lbr;
        struct evswitch         evswitch;
        struct perf_cpu_map     *cpus;
        struct perf_thread_map *threads;
@@ -1713,7 +1697,7 @@ static int perf_evlist__max_name_len(struct evlist *evlist)
        int max = 0;
 
        evlist__for_each_entry(evlist, evsel) {
-               int len = strlen(perf_evsel__name(evsel));
+               int len = strlen(evsel__name(evsel));
 
                max = MAX(len, max);
        }
@@ -1887,7 +1871,7 @@ static void process_event(struct perf_script *script,
                fprintf(fp, "%10" PRIu64 " ", sample->period);
 
        if (PRINT_FIELD(EVNAME)) {
-               const char *evname = perf_evsel__name(evsel);
+               const char *evname = evsel__name(evsel);
 
                if (!script->name_width)
                        script->name_width = perf_evlist__max_name_len(script->session->evlist);
@@ -1923,6 +1907,9 @@ static void process_event(struct perf_script *script,
        if (PRINT_FIELD(IP)) {
                struct callchain_cursor *cursor = NULL;
 
+               if (script->stitch_lbr)
+                       al->thread->lbr_stitch_enable = true;
+
                if (symbol_conf.use_callchain && sample->callchain &&
                    thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
                                              sample, NULL, NULL, scripting_max_stack) == 0)
@@ -1946,7 +1933,7 @@ static void process_event(struct perf_script *script,
        else if (PRINT_FIELD(BRSTACKOFF))
                perf_sample__fprintf_brstackoff(sample, thread, attr, fp);
 
-       if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
+       if (evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
                perf_sample__fprintf_bpf_output(sample, fp);
        perf_sample__fprintf_insn(sample, attr, thread, machine, fp);
 
@@ -1975,7 +1962,7 @@ static struct scripting_ops       *scripting_ops;
 static void __process_stat(struct evsel *counter, u64 tstamp)
 {
        int nthreads = perf_thread_map__nr(counter->core.threads);
-       int ncpus = perf_evsel__nr_cpus(counter);
+       int ncpus = evsel__nr_cpus(counter);
        int cpu, thread;
        static int header_printed;
 
@@ -2001,7 +1988,7 @@ static void __process_stat(struct evsel *counter, u64 tstamp)
                                counts->ena,
                                counts->run,
                                tstamp,
-                               perf_evsel__name(counter));
+                               evsel__name(counter));
                }
        }
 }
@@ -2040,7 +2027,7 @@ static int cleanup_scripting(void)
 
 static bool filter_cpu(struct perf_sample *sample)
 {
-       if (cpu_list)
+       if (cpu_list && sample->cpu != (u32)-1)
                return !test_bit(sample->cpu, cpu_bitmap);
        return false;
 }
@@ -2138,41 +2125,59 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
        return err;
 }
 
-static int process_comm_event(struct perf_tool *tool,
-                             union perf_event *event,
-                             struct perf_sample *sample,
-                             struct machine *machine)
+static int print_event_with_time(struct perf_tool *tool,
+                                union perf_event *event,
+                                struct perf_sample *sample,
+                                struct machine *machine,
+                                pid_t pid, pid_t tid, u64 timestamp)
 {
-       struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
        struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-       int ret = -1;
+       struct thread *thread = NULL;
 
-       thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing COMM event, skipping it.\n");
-               return -1;
+       if (evsel && !evsel->core.attr.sample_id_all) {
+               sample->cpu = 0;
+               sample->time = timestamp;
+               sample->pid = pid;
+               sample->tid = tid;
        }
 
-       if (perf_event__process_comm(tool, event, sample, machine) < 0)
-               goto out;
+       if (filter_cpu(sample))
+               return 0;
 
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-               sample->tid = event->comm.tid;
-               sample->pid = event->comm.pid;
-       }
-       if (!filter_cpu(sample)) {
+       if (tid != -1)
+               thread = machine__findnew_thread(machine, pid, tid);
+
+       if (thread && evsel) {
                perf_sample__fprintf_start(sample, thread, evsel,
-                                  PERF_RECORD_COMM, stdout);
-               perf_event__fprintf(event, stdout);
+                                          event->header.type, stdout);
        }
-       ret = 0;
-out:
+
+       perf_event__fprintf(event, stdout);
+
        thread__put(thread);
-       return ret;
+
+       return 0;
+}
+
+static int print_event(struct perf_tool *tool, union perf_event *event,
+                      struct perf_sample *sample, struct machine *machine,
+                      pid_t pid, pid_t tid)
+{
+       return print_event_with_time(tool, event, sample, machine, pid, tid, 0);
+}
+
+static int process_comm_event(struct perf_tool *tool,
+                             union perf_event *event,
+                             struct perf_sample *sample,
+                             struct machine *machine)
+{
+       if (perf_event__process_comm(tool, event, sample, machine) < 0)
+               return -1;
+
+       return print_event(tool, event, sample, machine, event->comm.pid,
+                          event->comm.tid);
 }
 
 static int process_namespaces_event(struct perf_tool *tool,
@@ -2180,37 +2185,11 @@ static int process_namespaces_event(struct perf_tool *tool,
                                    struct perf_sample *sample,
                                    struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-       int ret = -1;
-
-       thread = machine__findnew_thread(machine, event->namespaces.pid,
-                                        event->namespaces.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing NAMESPACES event, skipping it.\n");
-               return -1;
-       }
-
        if (perf_event__process_namespaces(tool, event, sample, machine) < 0)
-               goto out;
+               return -1;
 
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-               sample->tid = event->namespaces.tid;
-               sample->pid = event->namespaces.pid;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_NAMESPACES, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       ret = 0;
-out:
-       thread__put(thread);
-       return ret;
+       return print_event(tool, event, sample, machine, event->namespaces.pid,
+                          event->namespaces.tid);
 }
 
 static int process_cgroup_event(struct perf_tool *tool,
@@ -2218,34 +2197,11 @@ static int process_cgroup_event(struct perf_tool *tool,
                                struct perf_sample *sample,
                                struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-       int ret = -1;
-
-       thread = machine__findnew_thread(machine, sample->pid, sample->tid);
-       if (thread == NULL) {
-               pr_debug("problem processing CGROUP event, skipping it.\n");
-               return -1;
-       }
-
        if (perf_event__process_cgroup(tool, event, sample, machine) < 0)
-               goto out;
+               return -1;
 
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_CGROUP, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       ret = 0;
-out:
-       thread__put(thread);
-       return ret;
+       return print_event(tool, event, sample, machine, sample->pid,
+                           sample->tid);
 }
 
 static int process_fork_event(struct perf_tool *tool,
@@ -2253,69 +2209,24 @@ static int process_fork_event(struct perf_tool *tool,
                              struct perf_sample *sample,
                              struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-
        if (perf_event__process_fork(tool, event, sample, machine) < 0)
                return -1;
 
-       thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing FORK event, skipping it.\n");
-               return -1;
-       }
-
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = event->fork.time;
-               sample->tid = event->fork.tid;
-               sample->pid = event->fork.pid;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_FORK, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       thread__put(thread);
-
-       return 0;
+       return print_event_with_time(tool, event, sample, machine,
+                                    event->fork.pid, event->fork.tid,
+                                    event->fork.time);
 }
 static int process_exit_event(struct perf_tool *tool,
                              union perf_event *event,
                              struct perf_sample *sample,
                              struct machine *machine)
 {
-       int err = 0;
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-
-       thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing EXIT event, skipping it.\n");
+       /* Print before 'exit' deletes anything */
+       if (print_event_with_time(tool, event, sample, machine, event->fork.pid,
+                                 event->fork.tid, event->fork.time))
                return -1;
-       }
-
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-               sample->tid = event->fork.tid;
-               sample->pid = event->fork.pid;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_EXIT, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-
-       if (perf_event__process_exit(tool, event, sample, machine) < 0)
-               err = -1;
 
-       thread__put(thread);
-       return err;
+       return perf_event__process_exit(tool, event, sample, machine);
 }
 
 static int process_mmap_event(struct perf_tool *tool,
@@ -2323,33 +2234,11 @@ static int process_mmap_event(struct perf_tool *tool,
                              struct perf_sample *sample,
                              struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-
        if (perf_event__process_mmap(tool, event, sample, machine) < 0)
                return -1;
 
-       thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing MMAP event, skipping it.\n");
-               return -1;
-       }
-
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-               sample->tid = event->mmap.tid;
-               sample->pid = event->mmap.pid;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_MMAP, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       thread__put(thread);
-       return 0;
+       return print_event(tool, event, sample, machine, event->mmap.pid,
+                          event->mmap.tid);
 }
 
 static int process_mmap2_event(struct perf_tool *tool,
@@ -2357,33 +2246,11 @@ static int process_mmap2_event(struct perf_tool *tool,
                              struct perf_sample *sample,
                              struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-
        if (perf_event__process_mmap2(tool, event, sample, machine) < 0)
                return -1;
 
-       thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid);
-       if (thread == NULL) {
-               pr_debug("problem processing MMAP2 event, skipping it.\n");
-               return -1;
-       }
-
-       if (!evsel->core.attr.sample_id_all) {
-               sample->cpu = 0;
-               sample->time = 0;
-               sample->tid = event->mmap2.tid;
-               sample->pid = event->mmap2.pid;
-       }
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_MMAP2, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       thread__put(thread);
-       return 0;
+       return print_event(tool, event, sample, machine, event->mmap2.pid,
+                          event->mmap2.tid);
 }
 
 static int process_switch_event(struct perf_tool *tool,
@@ -2391,10 +2258,7 @@ static int process_switch_event(struct perf_tool *tool,
                                struct perf_sample *sample,
                                struct machine *machine)
 {
-       struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
 
        if (perf_event__process_switch(tool, event, sample, machine) < 0)
                return -1;
@@ -2405,20 +2269,8 @@ static int process_switch_event(struct perf_tool *tool,
        if (!script->show_switch_events)
                return 0;
 
-       thread = machine__findnew_thread(machine, sample->pid,
-                                        sample->tid);
-       if (thread == NULL) {
-               pr_debug("problem processing SWITCH event, skipping it.\n");
-               return -1;
-       }
-
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_SWITCH, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       thread__put(thread);
-       return 0;
+       return print_event(tool, event, sample, machine, sample->pid,
+                          sample->tid);
 }
 
 static int
@@ -2427,23 +2279,8 @@ process_lost_event(struct perf_tool *tool,
                   struct perf_sample *sample,
                   struct machine *machine)
 {
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-       struct thread *thread;
-
-       thread = machine__findnew_thread(machine, sample->pid,
-                                        sample->tid);
-       if (thread == NULL)
-               return -1;
-
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          PERF_RECORD_LOST, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-       thread__put(thread);
-       return 0;
+       return print_event(tool, event, sample, machine, sample->pid,
+                          sample->tid);
 }
 
 static int
@@ -2462,33 +2299,11 @@ process_bpf_events(struct perf_tool *tool __maybe_unused,
                   struct perf_sample *sample,
                   struct machine *machine)
 {
-       struct thread *thread;
-       struct perf_script *script = container_of(tool, struct perf_script, tool);
-       struct perf_session *session = script->session;
-       struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
-
        if (machine__process_ksymbol(machine, event, sample) < 0)
                return -1;
 
-       if (!evsel->core.attr.sample_id_all) {
-               perf_event__fprintf(event, stdout);
-               return 0;
-       }
-
-       thread = machine__findnew_thread(machine, sample->pid, sample->tid);
-       if (thread == NULL) {
-               pr_debug("problem processing MMAP event, skipping it.\n");
-               return -1;
-       }
-
-       if (!filter_cpu(sample)) {
-               perf_sample__fprintf_start(sample, thread, evsel,
-                                          event->header.type, stdout);
-               perf_event__fprintf(event, stdout);
-       }
-
-       thread__put(thread);
-       return 0;
+       return print_event(tool, event, sample, machine, sample->pid,
+                          sample->tid);
 }
 
 static void sig_handler(int sig __maybe_unused)
@@ -3145,7 +2960,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
 
                        match = 0;
                        evlist__for_each_entry(session->evlist, pos) {
-                               if (!strcmp(perf_evsel__name(pos), evname)) {
+                               if (!strcmp(evsel__name(pos), evname)) {
                                        match = 1;
                                        break;
                                }
@@ -3342,6 +3157,12 @@ static void script__setup_sample_type(struct perf_script *script)
                else
                        callchain_param.record_mode = CALLCHAIN_FP;
        }
+
+       if (script->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
+               pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
+                          "Please apply --call-graph lbr when recording.\n");
+               script->stitch_lbr = false;
+       }
 }
 
 static int process_stat_round_event(struct perf_session *session,
@@ -3653,6 +3474,8 @@ int cmd_script(int argc, const char **argv)
                   "file", "file saving guest os /proc/kallsyms"),
        OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules,
                   "file", "file saving guest os /proc/modules"),
+       OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr,
+                   "Enable LBR callgraph stitching approach"),
        OPTS_EVSWITCH(&script.evswitch),
        OPT_END()
        };
@@ -3709,7 +3532,7 @@ int cmd_script(int argc, const char **argv)
                return -1;
        }
 
-       if (itrace_synth_opts.callchain &&
+       if ((itrace_synth_opts.callchain || itrace_synth_opts.add_callchain) &&
            itrace_synth_opts.callchain_sz > scripting_max_stack)
                scripting_max_stack = itrace_synth_opts.callchain_sz;
 
index ec053dc1e35c82cede6708175d899f0ea7a3831d..e0c1ad23c76818f1747d1f18c7cf50f39c99d282 100644 (file)
@@ -238,9 +238,8 @@ static int write_stat_round_event(u64 tm, u64 type)
 
 #define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y)
 
-static int
-perf_evsel__write_stat_event(struct evsel *counter, u32 cpu, u32 thread,
-                            struct perf_counts_values *count)
+static int evsel__write_stat_event(struct evsel *counter, u32 cpu, u32 thread,
+                                  struct perf_counts_values *count)
 {
        struct perf_sample_id *sid = SID(counter, cpu, thread);
 
@@ -259,7 +258,7 @@ static int read_single_counter(struct evsel *counter, int cpu,
                count->val = val;
                return 0;
        }
-       return perf_evsel__read_counter(counter, cpu, thread);
+       return evsel__read_counter(counter, cpu, thread);
 }
 
 /*
@@ -284,7 +283,7 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu)
 
                /*
                 * The leader's group read loads data into its group members
-                * (via perf_evsel__read_counter()) and sets their count->loaded.
+                * (via evsel__read_counter()) and sets their count->loaded.
                 */
                if (!perf_counts__is_loaded(counter->counts, cpu, thread) &&
                    read_single_counter(counter, cpu, thread, rs)) {
@@ -297,7 +296,7 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu)
                perf_counts__set_loaded(counter->counts, cpu, thread, false);
 
                if (STAT_RECORD) {
-                       if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
+                       if (evsel__write_stat_event(counter, cpu, thread, count)) {
                                pr_err("failed to write stat event\n");
                                return -1;
                        }
@@ -306,7 +305,7 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu)
                if (verbose > 1) {
                        fprintf(stat_config.output,
                                "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
-                                       perf_evsel__name(counter),
+                                       evsel__name(counter),
                                        cpu,
                                        count->val, count->ena, count->run);
                }
@@ -359,6 +358,7 @@ static void process_interval(void)
        clock_gettime(CLOCK_MONOTONIC, &ts);
        diff_timespec(&rs, &ts, &ref_time);
 
+       perf_stat__reset_shadow_per_stat(&rt_stat);
        read_counters(&rs);
 
        if (STAT_RECORD) {
@@ -409,7 +409,7 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf
        workload_exec_errno = info->si_value.sival_int;
 }
 
-static bool perf_evsel__should_store_id(struct evsel *counter)
+static bool evsel__should_store_id(struct evsel *counter)
 {
        return STAT_RECORD || counter->core.attr.read_format & PERF_FORMAT_ID;
 }
@@ -454,7 +454,7 @@ static enum counter_recovery stat_handle_error(struct evsel *counter)
            errno == ENXIO) {
                if (verbose > 0)
                        ui__warning("%s event is not supported by the kernel.\n",
-                                   perf_evsel__name(counter));
+                                   evsel__name(counter));
                counter->supported = false;
                /*
                 * errored is a sticky flag that means one of the counter's
@@ -465,7 +465,7 @@ static enum counter_recovery stat_handle_error(struct evsel *counter)
                if ((counter->leader != counter) ||
                    !(counter->leader->core.nr_members > 1))
                        return COUNTER_SKIP;
-       } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
+       } else if (evsel__fallback(counter, errno, msg, sizeof(msg))) {
                if (verbose > 0)
                        ui__warning("%s\n", msg);
                return COUNTER_RETRY;
@@ -483,8 +483,7 @@ static enum counter_recovery stat_handle_error(struct evsel *counter)
                }
        }
 
-       perf_evsel__open_strerror(counter, &target,
-                                 errno, msg, sizeof(msg));
+       evsel__open_strerror(counter, &target, errno, msg, sizeof(msg));
        ui__error("%s\n", msg);
 
        if (child_pid != -1)
@@ -604,7 +603,7 @@ try_again:
                                if (!counter->reset_group)
                                        continue;
 try_again_reset:
-                               pr_debug2("reopening weak %s\n", perf_evsel__name(counter));
+                               pr_debug2("reopening weak %s\n", evsel__name(counter));
                                if (create_perf_stat_counter(counter, &stat_config, &target,
                                                             counter->cpu_iter - 1) < 0) {
 
@@ -635,14 +634,14 @@ try_again_reset:
                if (l > stat_config.unit_width)
                        stat_config.unit_width = l;
 
-               if (perf_evsel__should_store_id(counter) &&
-                   perf_evsel__store_ids(counter, evsel_list))
+               if (evsel__should_store_id(counter) &&
+                   evsel__store_ids(counter, evsel_list))
                        return -1;
        }
 
        if (perf_evlist__apply_filters(evsel_list, &counter)) {
                pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
-                       counter->filter, perf_evsel__name(counter), errno,
+                       counter->filter, evsel__name(counter), errno,
                        str_error_r(errno, msg, sizeof(msg)));
                return -1;
        }
@@ -686,8 +685,11 @@ try_again_reset:
                                        break;
                        }
                }
-               if (child_pid != -1)
+               if (child_pid != -1) {
+                       if (timeout)
+                               kill(child_pid, SIGTERM);
                        wait4(child_pid, &status, 0, &stat_config.ru_data);
+               }
 
                if (workload_exec_errno) {
                        const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg));
index 9e84fae9b096c9863585a9a35c175eea03946705..c76f84b174c4b2558a1be735a274f07d5bc5c51c 100644 (file)
@@ -579,8 +579,8 @@ process_sample_cpu_idle(struct timechart *tchart __maybe_unused,
                        struct perf_sample *sample,
                        const char *backtrace __maybe_unused)
 {
-       u32 state = perf_evsel__intval(evsel, sample, "state");
-       u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
+       u32 state  = evsel__intval(evsel, sample, "state");
+       u32 cpu_id = evsel__intval(evsel, sample, "cpu_id");
 
        if (state == (u32)PWR_EVENT_EXIT)
                c_state_end(tchart, cpu_id, sample->time);
@@ -595,8 +595,8 @@ process_sample_cpu_frequency(struct timechart *tchart,
                             struct perf_sample *sample,
                             const char *backtrace __maybe_unused)
 {
-       u32 state = perf_evsel__intval(evsel, sample, "state");
-       u32 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
+       u32 state  = evsel__intval(evsel, sample, "state");
+       u32 cpu_id = evsel__intval(evsel, sample, "cpu_id");
 
        p_state_change(tchart, cpu_id, sample->time, state);
        return 0;
@@ -608,9 +608,9 @@ process_sample_sched_wakeup(struct timechart *tchart,
                            struct perf_sample *sample,
                            const char *backtrace)
 {
-       u8 flags = perf_evsel__intval(evsel, sample, "common_flags");
-       int waker = perf_evsel__intval(evsel, sample, "common_pid");
-       int wakee = perf_evsel__intval(evsel, sample, "pid");
+       u8 flags  = evsel__intval(evsel, sample, "common_flags");
+       int waker = evsel__intval(evsel, sample, "common_pid");
+       int wakee = evsel__intval(evsel, sample, "pid");
 
        sched_wakeup(tchart, sample->cpu, sample->time, waker, wakee, flags, backtrace);
        return 0;
@@ -622,9 +622,9 @@ process_sample_sched_switch(struct timechart *tchart,
                            struct perf_sample *sample,
                            const char *backtrace)
 {
-       int prev_pid = perf_evsel__intval(evsel, sample, "prev_pid");
-       int next_pid = perf_evsel__intval(evsel, sample, "next_pid");
-       u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
+       int prev_pid   = evsel__intval(evsel, sample, "prev_pid");
+       int next_pid   = evsel__intval(evsel, sample, "next_pid");
+       u64 prev_state = evsel__intval(evsel, sample, "prev_state");
 
        sched_switch(tchart, sample->cpu, sample->time, prev_pid, next_pid,
                     prev_state, backtrace);
@@ -638,8 +638,8 @@ process_sample_power_start(struct timechart *tchart __maybe_unused,
                           struct perf_sample *sample,
                           const char *backtrace __maybe_unused)
 {
-       u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
-       u64 value = perf_evsel__intval(evsel, sample, "value");
+       u64 cpu_id = evsel__intval(evsel, sample, "cpu_id");
+       u64 value  = evsel__intval(evsel, sample, "value");
 
        c_state_start(cpu_id, sample->time, value);
        return 0;
@@ -661,8 +661,8 @@ process_sample_power_frequency(struct timechart *tchart,
                               struct perf_sample *sample,
                               const char *backtrace __maybe_unused)
 {
-       u64 cpu_id = perf_evsel__intval(evsel, sample, "cpu_id");
-       u64 value = perf_evsel__intval(evsel, sample, "value");
+       u64 cpu_id = evsel__intval(evsel, sample, "cpu_id");
+       u64 value  = evsel__intval(evsel, sample, "value");
 
        p_state_change(tchart, cpu_id, sample->time, value);
        return 0;
@@ -843,7 +843,7 @@ process_enter_read(struct timechart *tchart,
                   struct evsel *evsel,
                   struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ,
                                   sample->time, fd);
 }
@@ -853,7 +853,7 @@ process_exit_read(struct timechart *tchart,
                  struct evsel *evsel,
                  struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ,
                                 sample->time, ret);
 }
@@ -863,7 +863,7 @@ process_enter_write(struct timechart *tchart,
                    struct evsel *evsel,
                    struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE,
                                   sample->time, fd);
 }
@@ -873,7 +873,7 @@ process_exit_write(struct timechart *tchart,
                   struct evsel *evsel,
                   struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE,
                                 sample->time, ret);
 }
@@ -883,7 +883,7 @@ process_enter_sync(struct timechart *tchart,
                   struct evsel *evsel,
                   struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC,
                                   sample->time, fd);
 }
@@ -893,7 +893,7 @@ process_exit_sync(struct timechart *tchart,
                  struct evsel *evsel,
                  struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC,
                                 sample->time, ret);
 }
@@ -903,7 +903,7 @@ process_enter_tx(struct timechart *tchart,
                 struct evsel *evsel,
                 struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX,
                                   sample->time, fd);
 }
@@ -913,7 +913,7 @@ process_exit_tx(struct timechart *tchart,
                struct evsel *evsel,
                struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX,
                                 sample->time, ret);
 }
@@ -923,7 +923,7 @@ process_enter_rx(struct timechart *tchart,
                 struct evsel *evsel,
                 struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX,
                                   sample->time, fd);
 }
@@ -933,7 +933,7 @@ process_exit_rx(struct timechart *tchart,
                struct evsel *evsel,
                struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX,
                                 sample->time, ret);
 }
@@ -943,7 +943,7 @@ process_enter_poll(struct timechart *tchart,
                   struct evsel *evsel,
                   struct perf_sample *sample)
 {
-       long fd = perf_evsel__intval(evsel, sample, "fd");
+       long fd = evsel__intval(evsel, sample, "fd");
        return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL,
                                   sample->time, fd);
 }
@@ -953,7 +953,7 @@ process_exit_poll(struct timechart *tchart,
                  struct evsel *evsel,
                  struct perf_sample *sample)
 {
-       long ret = perf_evsel__intval(evsel, sample, "ret");
+       long ret = evsel__intval(evsel, sample, "ret");
        return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL,
                                 sample->time, ret);
 }
index 289cf83e658a371235fafdb61899077d5bb976e7..372c38254654d1a8bcf520f8deb2463db71a9c31 100644 (file)
@@ -33,6 +33,7 @@
 #include "util/map.h"
 #include "util/mmap.h"
 #include "util/session.h"
+#include "util/thread.h"
 #include "util/symbol.h"
 #include "util/synthetic-events.h"
 #include "util/top.h"
@@ -254,7 +255,7 @@ static void perf_top__show_details(struct perf_top *top)
        if (notes->src == NULL)
                goto out_unlock;
 
-       printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name);
+       printf("Showing %s for %s\n", evsel__name(top->sym_evsel), symbol->name);
        printf("  Events  Pcnt (>=%d%%)\n", top->annotation_opts.min_pcnt);
 
        more = symbol__annotate_printf(&he->ms, top->sym_evsel, &top->annotation_opts);
@@ -297,8 +298,7 @@ static void perf_top__resort_hists(struct perf_top *t)
                hists__collapse_resort(hists, NULL);
 
                /* Non-group events are considered as leader */
-               if (symbol_conf.event_group &&
-                   !perf_evsel__is_group_leader(pos)) {
+               if (symbol_conf.event_group && !evsel__is_group_leader(pos)) {
                        struct hists *leader_hists = evsel__hists(pos->leader);
 
                        hists__match(leader_hists, hists);
@@ -441,7 +441,7 @@ static void perf_top__print_mapped_keys(struct perf_top *top)
        fprintf(stdout, "\t[e]     display entries (lines).           \t(%d)\n", top->print_entries);
 
        if (top->evlist->core.nr_entries > 1)
-               fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", perf_evsel__name(top->sym_evsel));
+               fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", evsel__name(top->sym_evsel));
 
        fprintf(stdout, "\t[f]     profile display filter (count).    \t(%d)\n", top->count_filter);
 
@@ -528,13 +528,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
                                fprintf(stderr, "\nAvailable events:");
 
                                evlist__for_each_entry(top->evlist, top->sym_evsel)
-                                       fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, perf_evsel__name(top->sym_evsel));
+                                       fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, evsel__name(top->sym_evsel));
 
                                prompt_integer(&counter, "Enter details event counter");
 
                                if (counter >= top->evlist->core.nr_entries) {
                                        top->sym_evsel = evlist__first(top->evlist);
-                                       fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
+                                       fprintf(stderr, "Sorry, no such event, using %s.\n", evsel__name(top->sym_evsel));
                                        sleep(1);
                                        break;
                                }
@@ -775,6 +775,9 @@ static void perf_event__process_sample(struct perf_tool *tool,
        if (machine__resolve(machine, &al, sample) < 0)
                return;
 
+       if (top->stitch_lbr)
+               al.thread->lbr_stitch_enable = true;
+
        if (!machine->kptr_restrict_warned &&
            symbol_conf.kptr_restrict &&
            al.cpumode == PERF_RECORD_MISC_KERNEL) {
@@ -1042,14 +1045,13 @@ try_again:
                            perf_top_overwrite_fallback(top, counter))
                                goto try_again;
 
-                       if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) {
+                       if (evsel__fallback(counter, errno, msg, sizeof(msg))) {
                                if (verbose > 0)
                                        ui__warning("%s\n", msg);
                                goto try_again;
                        }
 
-                       perf_evsel__open_strerror(counter, &opts->target,
-                                                 errno, msg, sizeof(msg));
+                       evsel__open_strerror(counter, &opts->target, errno, msg, sizeof(msg));
                        ui__error("%s\n", msg);
                        goto out_err;
                }
@@ -1571,10 +1573,11 @@ int cmd_top(int argc, const char **argv)
                    "Sort the output by the event at the index n in group. "
                    "If n is invalid, sort by the first event. "
                    "WARNING: should be used on grouped events."),
+       OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
+                   "Enable LBR callgraph stitching approach"),
        OPTS_EVSWITCH(&top.evswitch),
        OPT_END()
        };
-       struct evlist *sb_evlist = NULL;
        const char * const top_usage[] = {
                "perf top [<options>]",
                NULL
@@ -1640,6 +1643,11 @@ int cmd_top(int argc, const char **argv)
                }
        }
 
+       if (top.stitch_lbr && !(callchain_param.record_mode == CALLCHAIN_LBR)) {
+               pr_err("Error: --stitch-lbr must be used with --call-graph lbr\n");
+               goto out_delete_evlist;
+       }
+
        if (opts->branch_stack && callchain_param.enabled)
                symbol_conf.show_branchflag_count = true;
 
@@ -1732,10 +1740,21 @@ int cmd_top(int argc, const char **argv)
                goto out_delete_evlist;
        }
 
-       if (!top.record_opts.no_bpf_event)
-               bpf_event__add_sb_event(&sb_evlist, &perf_env);
+       if (!top.record_opts.no_bpf_event) {
+               top.sb_evlist = evlist__new();
+
+               if (top.sb_evlist == NULL) {
+                       pr_err("Couldn't create side band evlist.\n.");
+                       goto out_delete_evlist;
+               }
+
+               if (evlist__add_bpf_sb_event(top.sb_evlist, &perf_env)) {
+                       pr_err("Couldn't ask for PERF_RECORD_BPF_EVENT side band events.\n.");
+                       goto out_delete_evlist;
+               }
+       }
 
-       if (perf_evlist__start_sb_thread(sb_evlist, target)) {
+       if (perf_evlist__start_sb_thread(top.sb_evlist, target)) {
                pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
                opts->no_bpf_event = true;
        }
@@ -1743,7 +1762,7 @@ int cmd_top(int argc, const char **argv)
        status = __cmd_top(&top);
 
        if (!opts->no_bpf_event)
-               perf_evlist__stop_sb_thread(sb_evlist);
+               perf_evlist__stop_sb_thread(top.sb_evlist);
 
 out_delete_evlist:
        evlist__delete(top.evlist);
index 01d542007c8b1210b9fbf348bfb8ccafb849c645..a46efb907bd420a8fd67512def192ddfd32ccfed 100644 (file)
@@ -366,11 +366,9 @@ out_delete:
        return NULL;
 }
 
-static int perf_evsel__init_tp_uint_field(struct evsel *evsel,
-                                         struct tp_field *field,
-                                         const char *name)
+static int evsel__init_tp_uint_field(struct evsel *evsel, struct tp_field *field, const char *name)
 {
-       struct tep_format_field *format_field = perf_evsel__field(evsel, name);
+       struct tep_format_field *format_field = evsel__field(evsel, name);
 
        if (format_field == NULL)
                return -1;
@@ -380,13 +378,11 @@ static int perf_evsel__init_tp_uint_field(struct evsel *evsel,
 
 #define perf_evsel__init_sc_tp_uint_field(evsel, name) \
        ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\
-          perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); })
+          evsel__init_tp_uint_field(evsel, &sc->name, #name); })
 
-static int perf_evsel__init_tp_ptr_field(struct evsel *evsel,
-                                        struct tp_field *field,
-                                        const char *name)
+static int evsel__init_tp_ptr_field(struct evsel *evsel, struct tp_field *field, const char *name)
 {
-       struct tep_format_field *format_field = perf_evsel__field(evsel, name);
+       struct tep_format_field *format_field = evsel__field(evsel, name);
 
        if (format_field == NULL)
                return -1;
@@ -396,7 +392,7 @@ static int perf_evsel__init_tp_ptr_field(struct evsel *evsel,
 
 #define perf_evsel__init_sc_tp_ptr_field(evsel, name) \
        ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\
-          perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); })
+          evsel__init_tp_ptr_field(evsel, &sc->name, #name); })
 
 static void evsel__delete_priv(struct evsel *evsel)
 {
@@ -404,13 +400,13 @@ static void evsel__delete_priv(struct evsel *evsel)
        evsel__delete(evsel);
 }
 
-static int perf_evsel__init_syscall_tp(struct evsel *evsel)
+static int evsel__init_syscall_tp(struct evsel *evsel)
 {
        struct syscall_tp *sc = evsel__syscall_tp(evsel);
 
        if (sc != NULL) {
-               if (perf_evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") &&
-                   perf_evsel__init_tp_uint_field(evsel, &sc->id, "nr"))
+               if (evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") &&
+                   evsel__init_tp_uint_field(evsel, &sc->id, "nr"))
                        return -ENOENT;
                return 0;
        }
@@ -418,14 +414,14 @@ static int perf_evsel__init_syscall_tp(struct evsel *evsel)
        return -ENOMEM;
 }
 
-static int perf_evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp)
+static int evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp)
 {
        struct syscall_tp *sc = evsel__syscall_tp(evsel);
 
        if (sc != NULL) {
-               struct tep_format_field *syscall_id = perf_evsel__field(tp, "id");
+               struct tep_format_field *syscall_id = evsel__field(tp, "id");
                if (syscall_id == NULL)
-                       syscall_id = perf_evsel__field(tp, "__syscall_nr");
+                       syscall_id = evsel__field(tp, "__syscall_nr");
                if (syscall_id == NULL ||
                    __tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap))
                        return -EINVAL;
@@ -436,21 +432,21 @@ static int perf_evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evs
        return -ENOMEM;
 }
 
-static int perf_evsel__init_augmented_syscall_tp_args(struct evsel *evsel)
+static int evsel__init_augmented_syscall_tp_args(struct evsel *evsel)
 {
        struct syscall_tp *sc = __evsel__syscall_tp(evsel);
 
        return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64));
 }
 
-static int perf_evsel__init_augmented_syscall_tp_ret(struct evsel *evsel)
+static int evsel__init_augmented_syscall_tp_ret(struct evsel *evsel)
 {
        struct syscall_tp *sc = __evsel__syscall_tp(evsel);
 
        return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap);
 }
 
-static int perf_evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler)
+static int evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler)
 {
        if (evsel__syscall_tp(evsel) != NULL) {
                if (perf_evsel__init_sc_tp_uint_field(evsel, id))
@@ -474,7 +470,7 @@ static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *
        if (IS_ERR(evsel))
                return NULL;
 
-       if (perf_evsel__init_raw_syscall_tp(evsel, handler))
+       if (evsel__init_raw_syscall_tp(evsel, handler))
                goto out_delete;
 
        return evsel;
@@ -1801,7 +1797,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
        return syscall__set_arg_fmts(sc);
 }
 
-static int perf_evsel__init_tp_arg_scnprintf(struct evsel *evsel)
+static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
 {
        struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel);
 
@@ -2074,7 +2070,7 @@ static struct syscall *trace__syscall_info(struct trace *trace,
                if (verbose > 1) {
                        static u64 n;
                        fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n",
-                               id, perf_evsel__name(evsel), ++n);
+                               id, evsel__name(evsel), ++n);
                }
                return NULL;
        }
@@ -2206,7 +2202,7 @@ static int trace__fprintf_sample(struct trace *trace, struct evsel *evsel,
                double ts = (double)sample->time / NSEC_PER_MSEC;
 
                printed += fprintf(trace->output, "%22s %10.3f %s %d/%d [%d]\n",
-                                  perf_evsel__name(evsel), ts,
+                                  evsel__name(evsel), ts,
                                   thread__comm_str(thread),
                                   sample->pid, sample->tid, sample->cpu);
        }
@@ -2382,7 +2378,7 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam
 
 static const char *errno_to_name(struct evsel *evsel, int err)
 {
-       struct perf_env *env = perf_evsel__env(evsel);
+       struct perf_env *env = evsel__env(evsel);
        const char *arch_name = perf_env__arch(env);
 
        return arch_syscalls__strerrno(arch_name, err);
@@ -2513,7 +2509,7 @@ errno_print: {
        if (callchain_ret > 0)
                trace__fprintf_callchain(trace, sample);
        else if (callchain_ret < 0)
-               pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel));
+               pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel));
 out:
        ttrace->entry_pending = false;
        err = 0;
@@ -2531,7 +2527,7 @@ static int trace__vfs_getname(struct trace *trace, struct evsel *evsel,
        size_t filename_len, entry_str_len, to_move;
        ssize_t remaining_space;
        char *pos;
-       const char *filename = perf_evsel__rawptr(evsel, sample, "pathname");
+       const char *filename = evsel__rawptr(evsel, sample, "pathname");
 
        if (!thread)
                goto out;
@@ -2587,7 +2583,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct evsel *evsel,
                                     union perf_event *event __maybe_unused,
                                     struct perf_sample *sample)
 {
-        u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
+        u64 runtime = evsel__intval(evsel, sample, "runtime");
        double runtime_ms = (double)runtime / NSEC_PER_MSEC;
        struct thread *thread = machine__findnew_thread(trace->host,
                                                        sample->pid,
@@ -2606,10 +2602,10 @@ out_put:
 out_dump:
        fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n",
               evsel->name,
-              perf_evsel__strval(evsel, sample, "comm"),
-              (pid_t)perf_evsel__intval(evsel, sample, "pid"),
+              evsel__strval(evsel, sample, "comm"),
+              (pid_t)evsel__intval(evsel, sample, "pid"),
               runtime,
-              perf_evsel__intval(evsel, sample, "vruntime"));
+              evsel__intval(evsel, sample, "vruntime"));
        goto out_put;
 }
 
@@ -2774,7 +2770,7 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel,
 
        fprintf(trace->output, "%s(", evsel->name);
 
-       if (perf_evsel__is_bpf_output(evsel)) {
+       if (evsel__is_bpf_output(evsel)) {
                bpf_output__fprintf(trace, sample);
        } else if (evsel->tp_format) {
                if (strncmp(evsel->tp_format->name, "sys_enter_", 10) ||
@@ -2795,7 +2791,7 @@ newline:
        if (callchain_ret > 0)
                trace__fprintf_callchain(trace, sample);
        else if (callchain_ret < 0)
-               pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel));
+               pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel));
 
        ++trace->nr_events_printed;
 
@@ -2890,7 +2886,7 @@ static int trace__pgfault(struct trace *trace,
        if (callchain_ret > 0)
                trace__fprintf_callchain(trace, sample);
        else if (callchain_ret < 0)
-               pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel));
+               pr_err("Problem processing %s callchain, skipping...\n", evsel__name(evsel));
 
        ++trace->nr_events_printed;
 out:
@@ -3032,10 +3028,10 @@ static bool evlist__add_vfs_getname(struct evlist *evlist)
        }
 
        evlist__for_each_entry_safe(evlist, evsel, tmp) {
-               if (!strstarts(perf_evsel__name(evsel), "probe:vfs_getname"))
+               if (!strstarts(evsel__name(evsel), "probe:vfs_getname"))
                        continue;
 
-               if (perf_evsel__field(evsel, "pathname")) {
+               if (evsel__field(evsel, "pathname")) {
                        evsel->handler = trace__vfs_getname;
                        found = true;
                        continue;
@@ -3093,7 +3089,7 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
        if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT &&
            sample->raw_data == NULL) {
                fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
-                      perf_evsel__name(evsel), sample->tid,
+                      evsel__name(evsel), sample->tid,
                       sample->cpu, sample->raw_size);
        } else {
                tracepoint_handler handler = evsel->handler;
@@ -3124,8 +3120,8 @@ static int trace__add_syscall_newtp(struct trace *trace)
        if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
                goto out_delete_sys_exit;
 
-       perf_evsel__config_callchain(sys_enter, &trace->opts, &callchain_param);
-       perf_evsel__config_callchain(sys_exit, &trace->opts, &callchain_param);
+       evsel__config_callchain(sys_enter, &trace->opts, &callchain_param);
+       evsel__config_callchain(sys_exit, &trace->opts, &callchain_param);
 
        evlist__add(evlist, sys_enter);
        evlist__add(evlist, sys_exit);
@@ -3164,10 +3160,9 @@ static int trace__set_ev_qualifier_tp_filter(struct trace *trace)
        if (filter == NULL)
                goto out_enomem;
 
-       if (!perf_evsel__append_tp_filter(trace->syscalls.events.sys_enter,
-                                         filter)) {
+       if (!evsel__append_tp_filter(trace->syscalls.events.sys_enter, filter)) {
                sys_exit = trace->syscalls.events.sys_exit;
-               err = perf_evsel__append_tp_filter(sys_exit, filter);
+               err = evsel__append_tp_filter(sys_exit, filter);
        }
 
        free(filter);
@@ -3695,7 +3690,7 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
        return __trace__deliver_event(trace, event->event);
 }
 
-static struct syscall_arg_fmt *perf_evsel__syscall_arg_fmt(struct evsel *evsel, char *arg)
+static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg)
 {
        struct tep_format_field *field;
        struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel);
@@ -3750,7 +3745,7 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
 
                        scnprintf(arg, sizeof(arg), "%.*s", left_size, left);
 
-                       fmt = perf_evsel__syscall_arg_fmt(evsel, arg);
+                       fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg);
                        if (fmt == NULL) {
                                pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n",
                                       arg, evsel->name, evsel->filter);
@@ -3801,7 +3796,7 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
 
        if (new_filter != evsel->filter) {
                pr_debug("New filter for %s: %s\n", evsel->name, new_filter);
-               perf_evsel__set_filter(evsel, new_filter);
+               evsel__set_filter(evsel, new_filter);
                free(new_filter);
        }
 
@@ -3849,7 +3844,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
                pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ);
                if (pgfault_maj == NULL)
                        goto out_error_mem;
-               perf_evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param);
+               evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param);
                evlist__add(evlist, pgfault_maj);
        }
 
@@ -3857,7 +3852,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
                pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN);
                if (pgfault_min == NULL)
                        goto out_error_mem;
-               perf_evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param);
+               evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param);
                evlist__add(evlist, pgfault_min);
        }
 
@@ -4108,7 +4103,7 @@ out_error:
 out_error_apply_filters:
        fprintf(trace->output,
                "Failed to set filter \"%s\" on event %s with %d (%s)\n",
-               evsel->filter, perf_evsel__name(evsel), errno,
+               evsel->filter, evsel__name(evsel), errno,
                str_error_r(errno, errbuf, sizeof(errbuf)));
        goto out_delete_evlist;
 }
@@ -4179,7 +4174,7 @@ static int trace__replay(struct trace *trace)
                                                             "syscalls:sys_enter");
 
        if (evsel &&
-           (perf_evsel__init_raw_syscall_tp(evsel, trace__sys_enter) < 0 ||
+           (evsel__init_raw_syscall_tp(evsel, trace__sys_enter) < 0 ||
            perf_evsel__init_sc_tp_ptr_field(evsel, args))) {
                pr_err("Error during initialize raw_syscalls:sys_enter event\n");
                goto out;
@@ -4191,7 +4186,7 @@ static int trace__replay(struct trace *trace)
                evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
                                                             "syscalls:sys_exit");
        if (evsel &&
-           (perf_evsel__init_raw_syscall_tp(evsel, trace__sys_exit) < 0 ||
+           (evsel__init_raw_syscall_tp(evsel, trace__sys_exit) < 0 ||
            perf_evsel__init_sc_tp_uint_field(evsel, ret))) {
                pr_err("Error during initialize raw_syscalls:sys_exit event\n");
                goto out;
@@ -4471,11 +4466,11 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist)
                        continue;
 
                if (strcmp(evsel->tp_format->system, "syscalls")) {
-                       perf_evsel__init_tp_arg_scnprintf(evsel);
+                       evsel__init_tp_arg_scnprintf(evsel);
                        continue;
                }
 
-               if (perf_evsel__init_syscall_tp(evsel))
+               if (evsel__init_syscall_tp(evsel))
                        return -1;
 
                if (!strncmp(evsel->tp_format->name, "sys_enter_", 10)) {
@@ -4989,7 +4984,7 @@ int cmd_trace(int argc, const char **argv)
         */
        if (trace.syscalls.events.augmented) {
                evlist__for_each_entry(trace.evlist, evsel) {
-                       bool raw_syscalls_sys_exit = strcmp(perf_evsel__name(evsel), "raw_syscalls:sys_exit") == 0;
+                       bool raw_syscalls_sys_exit = strcmp(evsel__name(evsel), "raw_syscalls:sys_exit") == 0;
 
                        if (raw_syscalls_sys_exit) {
                                trace.raw_augmented_syscalls = true;
@@ -4997,10 +4992,10 @@ int cmd_trace(int argc, const char **argv)
                        }
 
                        if (trace.syscalls.events.augmented->priv == NULL &&
-                           strstr(perf_evsel__name(evsel), "syscalls:sys_enter")) {
+                           strstr(evsel__name(evsel), "syscalls:sys_enter")) {
                                struct evsel *augmented = trace.syscalls.events.augmented;
-                               if (perf_evsel__init_augmented_syscall_tp(augmented, evsel) ||
-                                   perf_evsel__init_augmented_syscall_tp_args(augmented))
+                               if (evsel__init_augmented_syscall_tp(augmented, evsel) ||
+                                   evsel__init_augmented_syscall_tp_args(augmented))
                                        goto out;
                                /*
                                 * Augmented is __augmented_syscalls__ BPF_OUTPUT event
@@ -5014,16 +5009,16 @@ int cmd_trace(int argc, const char **argv)
                                 * as not to filter it, then we'll handle it just like we would
                                 * for the BPF_OUTPUT one:
                                 */
-                               if (perf_evsel__init_augmented_syscall_tp(evsel, evsel) ||
-                                   perf_evsel__init_augmented_syscall_tp_args(evsel))
+                               if (evsel__init_augmented_syscall_tp(evsel, evsel) ||
+                                   evsel__init_augmented_syscall_tp_args(evsel))
                                        goto out;
                                evsel->handler = trace__sys_enter;
                        }
 
-                       if (strstarts(perf_evsel__name(evsel), "syscalls:sys_exit_")) {
+                       if (strstarts(evsel__name(evsel), "syscalls:sys_exit_")) {
                                struct syscall_tp *sc;
 init_augmented_syscall_tp:
-                               if (perf_evsel__init_augmented_syscall_tp(evsel, evsel))
+                               if (evsel__init_augmented_syscall_tp(evsel, evsel))
                                        goto out;
                                sc = __evsel__syscall_tp(evsel);
                                /*
@@ -5047,7 +5042,7 @@ init_augmented_syscall_tp:
                                 */
                                if (trace.raw_augmented_syscalls)
                                        trace.raw_augmented_syscalls_args_size = (6 + 1) * sizeof(long) + sc->id.offset;
-                               perf_evsel__init_augmented_syscall_tp_ret(evsel);
+                               evsel__init_augmented_syscall_tp_ret(evsel);
                                evsel->handler = trace__sys_exit;
                        }
                }
index 0453ba26cdbd99bda32553eea810fb03832b6171..a42fab308ff6a2cbca76f65302efbc9f6c8f618b 100644 (file)
@@ -258,7 +258,8 @@ gets schedule to. Per task counters can be created by any user, for
 their own tasks.
 
 A 'pid == -1' and 'cpu == x' counter is a per CPU counter that counts
-all events on CPU-x. Per CPU counters need CAP_SYS_ADMIN privilege.
+all events on CPU-x. Per CPU counters need CAP_PERFMON or CAP_SYS_ADMIN
+privilege.
 
 The 'flags' parameter is currently unused and must be zero.
 
diff --git a/tools/perf/pmu-events/arch/powerpc/power9/nest_metrics.json b/tools/perf/pmu-events/arch/powerpc/power9/nest_metrics.json
new file mode 100644 (file)
index 0000000..c121e52
--- /dev/null
@@ -0,0 +1,19 @@
+[
+    {
+        "MetricExpr": "(hv_24x7@PM_MCS01_128B_RD_DISP_PORT01\\,chip\\=?@ + hv_24x7@PM_MCS01_128B_RD_DISP_PORT23\\,chip\\=?@ + hv_24x7@PM_MCS23_128B_RD_DISP_PORT01\\,chip\\=?@ + hv_24x7@PM_MCS23_128B_RD_DISP_PORT23\\,chip\\=?@)",
+        "MetricName": "Memory_RD_BW_Chip",
+        "MetricGroup": "Memory_BW",
+        "ScaleUnit": "1.6e-2MB"
+    },
+    {
+       "MetricExpr": "(hv_24x7@PM_MCS01_128B_WR_DISP_PORT01\\,chip\\=?@ + hv_24x7@PM_MCS01_128B_WR_DISP_PORT23\\,chip\\=?@ + hv_24x7@PM_MCS23_128B_WR_DISP_PORT01\\,chip\\=?@ + hv_24x7@PM_MCS23_128B_WR_DISP_PORT23\\,chip\\=?@ )",
+        "MetricName": "Memory_WR_BW_Chip",
+        "MetricGroup": "Memory_BW",
+        "ScaleUnit": "1.6e-2MB"
+    },
+    {
+       "MetricExpr": "(hv_24x7@PM_PB_CYC\\,chip\\=?@ )",
+        "MetricName": "PowerBUS_Frequency",
+        "ScaleUnit": "2.5e-7GHz"
+    }
+]
index 53e76d5d5b37d039a2a87145f8b6f541362ed917..c8f306b572f4906767e7cb5c6aad07d884b58a3c 100644 (file)
@@ -26,7 +26,7 @@ struct pmu_event {
  * Map a CPU to its table of PMU events. The CPU is identified by the
  * cpuid field, which is an arch-specific identifier for the CPU.
  * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile
- * must match the get_cpustr() in tools/perf/arch/xxx/util/header.c)
+ * must match the get_cpuid_str() in tools/perf/arch/xxx/util/header.c)
  *
  * The  cpuid can contain any character other than the comma.
  */
diff --git a/tools/perf/scripts/python/bin/flamegraph-record b/tools/perf/scripts/python/bin/flamegraph-record
new file mode 100755 (executable)
index 0000000..7df5a19
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -g "$@"
diff --git a/tools/perf/scripts/python/bin/flamegraph-report b/tools/perf/scripts/python/bin/flamegraph-report
new file mode 100755 (executable)
index 0000000..53c5dc9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: create flame graphs
+perf script -s "$PERF_EXEC_PATH"/scripts/python/flamegraph.py -- "$@"
diff --git a/tools/perf/scripts/python/flamegraph.py b/tools/perf/scripts/python/flamegraph.py
new file mode 100755 (executable)
index 0000000..61f3be9
--- /dev/null
@@ -0,0 +1,124 @@
+# flamegraph.py - create flame graphs from perf samples
+# SPDX-License-Identifier: GPL-2.0
+#
+# Usage:
+#
+#     perf record -a -g -F 99 sleep 60
+#     perf script report flamegraph
+#
+# Combined:
+#
+#     perf script flamegraph -a -F 99 sleep 60
+#
+# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
+# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
+# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
+
+from __future__ import print_function
+import sys
+import os
+import argparse
+import json
+
+
+class Node:
+    def __init__(self, name, libtype=""):
+        self.name = name
+        self.libtype = libtype
+        self.value = 0
+        self.children = []
+
+    def toJSON(self):
+        return {
+            "n": self.name,
+            "l": self.libtype,
+            "v": self.value,
+            "c": self.children
+        }
+
+
+class FlameGraphCLI:
+    def __init__(self, args):
+        self.args = args
+        self.stack = Node("root")
+
+        if self.args.format == "html" and \
+                not os.path.isfile(self.args.template):
+            print("Flame Graph template {} does not exist. Please install "
+                  "the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) "
+                  "package, specify an existing flame graph template "
+                  "(--template PATH) or another output format "
+                  "(--format FORMAT).".format(self.args.template),
+                  file=sys.stderr)
+            sys.exit(1)
+
+    def find_or_create_node(self, node, name, dso):
+        libtype = "kernel" if dso == "[kernel.kallsyms]" else ""
+        if name is None:
+            name = "[unknown]"
+
+        for child in node.children:
+            if child.name == name and child.libtype == libtype:
+                return child
+
+        child = Node(name, libtype)
+        node.children.append(child)
+        return child
+
+    def process_event(self, event):
+        node = self.find_or_create_node(self.stack, event["comm"], None)
+        if "callchain" in event:
+            for entry in reversed(event['callchain']):
+                node = self.find_or_create_node(
+                    node, entry.get("sym", {}).get("name"), event.get("dso"))
+        else:
+            node = self.find_or_create_node(
+                node, entry.get("symbol"), event.get("dso"))
+        node.value += 1
+
+    def trace_end(self):
+        json_str = json.dumps(self.stack, default=lambda x: x.toJSON())
+
+        if self.args.format == "html":
+            try:
+                with open(self.args.template) as f:
+                    output_str = f.read().replace("/** @flamegraph_json **/",
+                                                  json_str)
+            except IOError as e:
+                print("Error reading template file: {}".format(e), file=sys.stderr)
+                sys.exit(1)
+            output_fn = self.args.output or "flamegraph.html"
+        else:
+            output_str = json_str
+            output_fn = self.args.output or "stacks.json"
+
+        if output_fn == "-":
+            sys.stdout.write(output_str)
+        else:
+            print("dumping data to {}".format(output_fn))
+            try:
+                with open(output_fn, "w") as out:
+                    out.write(output_str)
+            except IOError as e:
+                print("Error writing output file: {}".format(e), file=sys.stderr)
+                sys.exit(1)
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Create flame graphs.")
+    parser.add_argument("-f", "--format",
+                        default="html", choices=["json", "html"],
+                        help="output file format")
+    parser.add_argument("-o", "--output",
+                        help="output file name")
+    parser.add_argument("--template",
+                        default="/usr/share/d3-flame-graph/d3-flamegraph-base.html",
+                        help="path to flamegraph HTML template")
+    parser.add_argument("-i", "--input",
+                        help=argparse.SUPPRESS)
+
+    args = parser.parse_args()
+    cli = FlameGraphCLI(args)
+
+    process_event = cli.process_event
+    trace_end = cli.trace_end
index b3d1bf13ca07685e822599e7a24931407864d5ae..c75557aeef0e7580f87fae74fda008caf42d5ffc 100644 (file)
@@ -56,6 +56,7 @@ perf-y += mem2node.o
 perf-y += maps.o
 perf-y += time-utils-test.o
 perf-y += genelf.o
+perf-y += api-io.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
        $(call rule_mkdir)
diff --git a/tools/perf/tests/api-io.c b/tools/perf/tests/api-io.c
new file mode 100644 (file)
index 0000000..2ada86a
--- /dev/null
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "tests.h"
+#include <api/io.h>
+#include <linux/kernel.h>
+
+#define TEMPL "/tmp/perf-test-XXXXXX"
+
+#define EXPECT_EQUAL(val, expected)                             \
+do {                                                           \
+       if (val != expected) {                                  \
+               pr_debug("%s:%d: %d != %d\n",                   \
+                       __FILE__, __LINE__, val, expected);     \
+               ret = -1;                                       \
+       }                                                       \
+} while (0)
+
+#define EXPECT_EQUAL64(val, expected)                           \
+do {                                                           \
+       if (val != expected) {                                  \
+               pr_debug("%s:%d: %lld != %lld\n",               \
+                       __FILE__, __LINE__, val, expected);     \
+               ret = -1;                                       \
+       }                                                       \
+} while (0)
+
+static int make_test_file(char path[PATH_MAX], const char *contents)
+{
+       ssize_t contents_len = strlen(contents);
+       int fd;
+
+       strcpy(path, TEMPL);
+       fd = mkstemp(path);
+       if (fd < 0) {
+               pr_debug("mkstemp failed");
+               return -1;
+       }
+       if (write(fd, contents, contents_len) < contents_len) {
+               pr_debug("short write");
+               close(fd);
+               unlink(path);
+               return -1;
+       }
+       close(fd);
+       return 0;
+}
+
+static int setup_test(char path[PATH_MAX], const char *contents,
+                     size_t buf_size, struct io *io)
+{
+       if (make_test_file(path, contents))
+               return -1;
+
+       io->fd = open(path, O_RDONLY);
+       if (io->fd < 0) {
+               pr_debug("Failed to open '%s'\n", path);
+               unlink(path);
+               return -1;
+       }
+       io->buf = malloc(buf_size);
+       if (io->buf == NULL) {
+               pr_debug("Failed to allocate memory");
+               close(io->fd);
+               unlink(path);
+               return -1;
+       }
+       io__init(io, io->fd, io->buf, buf_size);
+       return 0;
+}
+
+static void cleanup_test(char path[PATH_MAX], struct io *io)
+{
+       free(io->buf);
+       close(io->fd);
+       unlink(path);
+}
+
+static int do_test_get_char(const char *test_string, size_t buf_size)
+{
+       char path[PATH_MAX];
+       struct io io;
+       int ch, ret = 0;
+       size_t i;
+
+       if (setup_test(path, test_string, buf_size, &io))
+               return -1;
+
+       for (i = 0; i < strlen(test_string); i++) {
+               ch = io__get_char(&io);
+
+               EXPECT_EQUAL(ch, test_string[i]);
+               EXPECT_EQUAL(io.eof, false);
+       }
+       ch = io__get_char(&io);
+       EXPECT_EQUAL(ch, -1);
+       EXPECT_EQUAL(io.eof, true);
+
+       cleanup_test(path, &io);
+       return ret;
+}
+
+static int test_get_char(void)
+{
+       int i, ret = 0;
+       size_t j;
+
+       static const char *const test_strings[] = {
+               "12345678abcdef90",
+               "a\nb\nc\nd\n",
+               "\a\b\t\v\f\r",
+       };
+       for (i = 0; i <= 10; i++) {
+               for (j = 0; j < ARRAY_SIZE(test_strings); j++) {
+                       if (do_test_get_char(test_strings[j], 1 << i))
+                               ret = -1;
+               }
+       }
+       return ret;
+}
+
+static int do_test_get_hex(const char *test_string,
+                       __u64 val1, int ch1,
+                       __u64 val2, int ch2,
+                       __u64 val3, int ch3,
+                       bool end_eof)
+{
+       char path[PATH_MAX];
+       struct io io;
+       int ch, ret = 0;
+       __u64 hex;
+
+       if (setup_test(path, test_string, 4, &io))
+               return -1;
+
+       ch = io__get_hex(&io, &hex);
+       EXPECT_EQUAL64(hex, val1);
+       EXPECT_EQUAL(ch, ch1);
+
+       ch = io__get_hex(&io, &hex);
+       EXPECT_EQUAL64(hex, val2);
+       EXPECT_EQUAL(ch, ch2);
+
+       ch = io__get_hex(&io, &hex);
+       EXPECT_EQUAL64(hex, val3);
+       EXPECT_EQUAL(ch, ch3);
+
+       EXPECT_EQUAL(io.eof, end_eof);
+
+       cleanup_test(path, &io);
+       return ret;
+}
+
+static int test_get_hex(void)
+{
+       int ret = 0;
+
+       if (do_test_get_hex("12345678abcdef90",
+                               0x12345678abcdef90, -1,
+                               0, -1,
+                               0, -1,
+                               true))
+               ret = -1;
+
+       if (do_test_get_hex("1\n2\n3\n",
+                               1, '\n',
+                               2, '\n',
+                               3, '\n',
+                               false))
+               ret = -1;
+
+       if (do_test_get_hex("12345678ABCDEF90;a;b",
+                               0x12345678abcdef90, ';',
+                               0xa, ';',
+                               0xb, -1,
+                               true))
+               ret = -1;
+
+       if (do_test_get_hex("0x1x2x",
+                               0, 'x',
+                               1, 'x',
+                               2, 'x',
+                               false))
+               ret = -1;
+
+       if (do_test_get_hex("x1x",
+                               0, -2,
+                               1, 'x',
+                               0, -1,
+                               true))
+               ret = -1;
+
+       if (do_test_get_hex("10000000000000000000000000000abcdefgh99i",
+                               0xabcdef, 'g',
+                               0, -2,
+                               0x99, 'i',
+                               false))
+               ret = -1;
+
+       return ret;
+}
+
+static int do_test_get_dec(const char *test_string,
+                       __u64 val1, int ch1,
+                       __u64 val2, int ch2,
+                       __u64 val3, int ch3,
+                       bool end_eof)
+{
+       char path[PATH_MAX];
+       struct io io;
+       int ch, ret = 0;
+       __u64 dec;
+
+       if (setup_test(path, test_string, 4, &io))
+               return -1;
+
+       ch = io__get_dec(&io, &dec);
+       EXPECT_EQUAL64(dec, val1);
+       EXPECT_EQUAL(ch, ch1);
+
+       ch = io__get_dec(&io, &dec);
+       EXPECT_EQUAL64(dec, val2);
+       EXPECT_EQUAL(ch, ch2);
+
+       ch = io__get_dec(&io, &dec);
+       EXPECT_EQUAL64(dec, val3);
+       EXPECT_EQUAL(ch, ch3);
+
+       EXPECT_EQUAL(io.eof, end_eof);
+
+       cleanup_test(path, &io);
+       return ret;
+}
+
+static int test_get_dec(void)
+{
+       int ret = 0;
+
+       if (do_test_get_dec("12345678abcdef90",
+                               12345678, 'a',
+                               0, -2,
+                               0, -2,
+                               false))
+               ret = -1;
+
+       if (do_test_get_dec("1\n2\n3\n",
+                               1, '\n',
+                               2, '\n',
+                               3, '\n',
+                               false))
+               ret = -1;
+
+       if (do_test_get_dec("12345678;1;2",
+                               12345678, ';',
+                               1, ';',
+                               2, -1,
+                               true))
+               ret = -1;
+
+       if (do_test_get_dec("0x1x2x",
+                               0, 'x',
+                               1, 'x',
+                               2, 'x',
+                               false))
+               ret = -1;
+
+       if (do_test_get_dec("x1x",
+                               0, -2,
+                               1, 'x',
+                               0, -1,
+                               true))
+               ret = -1;
+
+       if (do_test_get_dec("10000000000000000000000000000000000000000000000000000000000123456789ab99c",
+                               123456789, 'a',
+                               0, -2,
+                               99, 'c',
+                               false))
+               ret = -1;
+
+       return ret;
+}
+
+int test__api_io(struct test *test __maybe_unused,
+               int subtest __maybe_unused)
+{
+       int ret = 0;
+
+       if (test_get_char())
+               ret = TEST_FAIL;
+       if (test_get_hex())
+               ret = TEST_FAIL;
+       if (test_get_dec())
+               ret = TEST_FAIL;
+       return ret;
+}
index b6322eb0f4236062977683dcdd6a54ef13369f54..3471ec52ea11cb29034913d8515c91d7f735685f 100644 (file)
@@ -309,6 +309,10 @@ static struct test generic_tests[] = {
                .desc = "Test jit_write_elf",
                .func = test__jit_write_elf,
        },
+       {
+               .desc = "Test api io",
+               .func = test__api_io,
+       },
        {
                .desc = "maps__merge_in",
                .func = test__maps__merge_in,
index 1e8a9f5c356dd623226c5fb7dee5e4f30b002b6f..db68894a6f40a9fd72e57cce00fee6f58fbd1294 100644 (file)
@@ -72,7 +72,7 @@ static int attach__current_disabled(struct evlist *evlist)
 
        evsel->core.attr.disabled = 1;
 
-       err = perf_evsel__open_per_thread(evsel, threads);
+       err = evsel__open_per_thread(evsel, threads);
        if (err) {
                pr_debug("Failed to open event cpu-clock:u\n");
                return err;
@@ -96,7 +96,7 @@ static int attach__current_enabled(struct evlist *evlist)
                return -1;
        }
 
-       err = perf_evsel__open_per_thread(evsel, threads);
+       err = evsel__open_per_thread(evsel, threads);
 
        perf_thread_map__put(threads);
        return err == 0 ? TEST_OK : TEST_FAIL;
@@ -125,7 +125,7 @@ static int attach__cpu_disabled(struct evlist *evlist)
 
        evsel->core.attr.disabled = 1;
 
-       err = perf_evsel__open_per_cpu(evsel, cpus, -1);
+       err = evsel__open_per_cpu(evsel, cpus, -1);
        if (err) {
                if (err == -EACCES)
                        return TEST_SKIP;
@@ -152,7 +152,7 @@ static int attach__cpu_enabled(struct evlist *evlist)
                return -1;
        }
 
-       err = perf_evsel__open_per_cpu(evsel, cpus, -1);
+       err = evsel__open_per_cpu(evsel, cpus, -1);
        if (err == -EACCES)
                return TEST_SKIP;
 
index c727379cf20e1956009a858fe4961c12d4ac9171..bdcf032f85162ea2694eca399a6a79e95885908c 100644 (file)
@@ -109,7 +109,7 @@ int test__event_update(struct test *test __maybe_unused, int subtest __maybe_unu
        TEST_ASSERT_VAL("failed to synthesize attr update scale",
                        !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale));
 
-       tmp.name = perf_evsel__name(evsel);
+       tmp.name = evsel__name(evsel);
 
        TEST_ASSERT_VAL("failed to synthesize attr update name",
                        !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name));
index 956205bf932651949b2793869c35199c4de8a479..61ecd8e33a017bde3e2ea17f026eb9196c45375a 100644 (file)
@@ -20,12 +20,11 @@ static int perf_evsel__roundtrip_cache_name_test(void)
        for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
                for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
                        /* skip invalid cache type */
-                       if (!perf_evsel__is_cache_op_valid(type, op))
+                       if (!evsel__is_cache_op_valid(type, op))
                                continue;
 
                        for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
-                               __perf_evsel__hw_cache_type_op_res_name(type, op, i,
-                                                                       name, sizeof(name));
+                               __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
                                err = parse_events(evlist, name, NULL);
                                if (err)
                                        ret = err;
@@ -39,23 +38,22 @@ static int perf_evsel__roundtrip_cache_name_test(void)
        for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
                for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
                        /* skip invalid cache type */
-                       if (!perf_evsel__is_cache_op_valid(type, op))
+                       if (!evsel__is_cache_op_valid(type, op))
                                continue;
 
                        for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
-                               __perf_evsel__hw_cache_type_op_res_name(type, op, i,
-                                                                       name, sizeof(name));
+                               __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
                                if (evsel->idx != idx)
                                        continue;
 
                                ++idx;
 
-                               if (strcmp(perf_evsel__name(evsel), name)) {
-                                       pr_debug("%s != %s\n", perf_evsel__name(evsel), name);
+                               if (strcmp(evsel__name(evsel), name)) {
+                                       pr_debug("%s != %s\n", evsel__name(evsel), name);
                                        ret = -1;
                                }
 
-                               evsel = perf_evsel__next(evsel);
+                               evsel = evsel__next(evsel);
                        }
                }
        }
@@ -84,9 +82,9 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
 
        err = 0;
        evlist__for_each_entry(evlist, evsel) {
-               if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) {
+               if (strcmp(evsel__name(evsel), names[evsel->idx])) {
                        --err;
-                       pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]);
+                       pr_debug("%s != %s\n", evsel__name(evsel), names[evsel->idx]);
                }
        }
 
index 261e6eaaee99d6394a8a2a3ed6d079f755af29ca..ce8aa32bc3ee692cb6aa8c162d18fb0d7b59c657 100644 (file)
@@ -8,7 +8,7 @@
 static int perf_evsel__test_field(struct evsel *evsel, const char *name,
                                  int size, bool should_be_signed)
 {
-       struct tep_format_field *field = perf_evsel__field(evsel, name);
+       struct tep_format_field *field = evsel__field(evsel, name);
        int is_signed;
        int ret = 0;
 
index 28313e59d6f6ab959184efc45de3b62b41a2a75a..f9e8e562883636be05765c7d431faaac259d60ff 100644 (file)
@@ -6,11 +6,11 @@
 #include <string.h>
 #include <linux/zalloc.h>
 
-static int test(struct parse_ctx *ctx, const char *e, double val2)
+static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
        double val;
 
-       if (expr__parse(&val, ctx, e))
+       if (expr__parse(&val, ctx, e, 1))
                TEST_ASSERT_VAL("parse test failed", 0);
        TEST_ASSERT_VAL("unexpected value", val == val2);
        return 0;
@@ -22,7 +22,7 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
        const char **other;
        double val;
        int i, ret;
-       struct parse_ctx ctx;
+       struct expr_parse_ctx ctx;
        int num_other;
 
        expr__ctx_init(&ctx);
@@ -44,21 +44,29 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
                return ret;
 
        p = "FOO/0";
-       ret = expr__parse(&val, &ctx, p);
+       ret = expr__parse(&val, &ctx, p, 1);
        TEST_ASSERT_VAL("division by zero", ret == -1);
 
        p = "BAR/";
-       ret = expr__parse(&val, &ctx, p);
+       ret = expr__parse(&val, &ctx, p, 1);
        TEST_ASSERT_VAL("missing operand", ret == -1);
 
        TEST_ASSERT_VAL("find other",
-                       expr__find_other("FOO + BAR + BAZ + BOZO", "FOO", &other, &num_other) == 0);
+                       expr__find_other("FOO + BAR + BAZ + BOZO", "FOO", &other, &num_other, 1) == 0);
        TEST_ASSERT_VAL("find other", num_other == 3);
        TEST_ASSERT_VAL("find other", !strcmp(other[0], "BAR"));
        TEST_ASSERT_VAL("find other", !strcmp(other[1], "BAZ"));
        TEST_ASSERT_VAL("find other", !strcmp(other[2], "BOZO"));
        TEST_ASSERT_VAL("find other", other[3] == NULL);
 
+       TEST_ASSERT_VAL("find other",
+                       expr__find_other("EVENT1\\,param\\=?@ + EVENT2\\,param\\=?@", NULL,
+                                  &other, &num_other, 3) == 0);
+       TEST_ASSERT_VAL("find other", num_other == 2);
+       TEST_ASSERT_VAL("find other", !strcmp(other[0], "EVENT1,param=3/"));
+       TEST_ASSERT_VAL("find other", !strcmp(other[1], "EVENT2,param=3/"));
+       TEST_ASSERT_VAL("find other", other[2] == NULL);
+
        for (i = 0; i < num_other; i++)
                zfree(&other[i]);
        free((void *)other);
index 6367c8f6ca22f80cd340b0668c2a5d1fe476cbd0..7a542f1c1c78477c1715cb6b91010298d3248ab5 100644 (file)
@@ -280,7 +280,7 @@ static int test1(struct evsel *evsel, struct machine *machine)
 
        symbol_conf.use_callchain = false;
        symbol_conf.cumulate_callchain = false;
-       perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
+       evsel__reset_sample_bit(evsel, CALLCHAIN);
 
        setup_sorting(NULL);
        callchain_register_param(&callchain_param);
@@ -427,7 +427,7 @@ static int test2(struct evsel *evsel, struct machine *machine)
 
        symbol_conf.use_callchain = true;
        symbol_conf.cumulate_callchain = false;
-       perf_evsel__set_sample_bit(evsel, CALLCHAIN);
+       evsel__set_sample_bit(evsel, CALLCHAIN);
 
        setup_sorting(NULL);
        callchain_register_param(&callchain_param);
@@ -485,7 +485,7 @@ static int test3(struct evsel *evsel, struct machine *machine)
 
        symbol_conf.use_callchain = false;
        symbol_conf.cumulate_callchain = true;
-       perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
+       evsel__reset_sample_bit(evsel, CALLCHAIN);
 
        setup_sorting(NULL);
        callchain_register_param(&callchain_param);
@@ -669,7 +669,7 @@ static int test4(struct evsel *evsel, struct machine *machine)
 
        symbol_conf.use_callchain = true;
        symbol_conf.cumulate_callchain = true;
-       perf_evsel__set_sample_bit(evsel, CALLCHAIN);
+       evsel__set_sample_bit(evsel, CALLCHAIN);
 
        setup_sorting(NULL);
 
index 5f4c0dbb471582179ea301dec2678f7bf3e94401..d4b8eb6e337a2d0cd245f6420cbdc9df4d5da835 100644 (file)
@@ -86,7 +86,7 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
                }
 
                evsels[i]->core.attr.wakeup_events = 1;
-               perf_evsel__set_sample_id(evsels[i], false);
+               evsel__set_sample_id(evsels[i], false);
 
                evlist__add(evlist, evsels[i]);
 
@@ -150,7 +150,7 @@ out_init:
                if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
                        pr_debug("expected %d %s events, got %d\n",
                                 expected_nr_events[evsel->idx],
-                                perf_evsel__name(evsel), nr_events[evsel->idx]);
+                                evsel__name(evsel), nr_events[evsel->idx]);
                        err = -1;
                        goto out_delete_evlist;
                }
index 93c176523e385d8a97a837ab8bc76fcbda5479e1..900934be22d2537949004d56a354f224a9a3067b 100644 (file)
@@ -103,15 +103,15 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
                if (cpus->map[cpu] >= CPU_SETSIZE)
                        continue;
 
-               if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
-                       pr_debug("perf_evsel__read_on_cpu\n");
+               if (evsel__read_on_cpu(evsel, cpu, 0) < 0) {
+                       pr_debug("evsel__read_on_cpu\n");
                        err = -1;
                        break;
                }
 
                expected = nr_openat_calls + cpu;
                if (perf_counts(evsel->counts, cpu, 0)->val != expected) {
-                       pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
+                       pr_debug("evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
                                 expected, cpus->map[cpu], perf_counts(evsel->counts, cpu, 0)->val);
                        err = -1;
                }
index c6b2d7aab608fac947679a07f61b7c3a3dd0e10e..1dc2897d2df926aaec51da03e0e43490f5a00a23 100644 (file)
@@ -60,7 +60,7 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
                goto out_delete_evlist;
        }
 
-       perf_evsel__config(evsel, &opts, NULL);
+       evsel__config(evsel, &opts, NULL);
 
        perf_thread_map__set_pid(evlist->core.threads, 0, getpid());
 
@@ -108,13 +108,13 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
                                        continue;
                                }
 
-                               err = perf_evsel__parse_sample(evsel, event, &sample);
+                               err = evsel__parse_sample(evsel, event, &sample);
                                if (err) {
                                        pr_debug("Can't parse sample, err = %d\n", err);
                                        goto out_delete_evlist;
                                }
 
-                               tp_flags = perf_evsel__intval(evsel, &sample, "flags");
+                               tp_flags = evsel__intval(evsel, &sample, "flags");
 
                                if (flags != tp_flags) {
                                        pr_debug("%s: Expected flags=%#x, got %#x\n",
index 5ebffae186051f8213a408890d9787ebfb9c39a0..db5d8bb8cd06ca41f3274cb2a66190aa3473c412 100644 (file)
@@ -34,7 +34,7 @@ int test__openat_syscall_event(struct test *test __maybe_unused, int subtest __m
                goto out_thread_map_delete;
        }
 
-       if (perf_evsel__open_per_thread(evsel, threads) < 0) {
+       if (evsel__open_per_thread(evsel, threads) < 0) {
                pr_debug("failed to open counter: %s, "
                         "tweak /proc/sys/kernel/perf_event_paranoid?\n",
                         str_error_r(errno, sbuf, sizeof(sbuf)));
@@ -46,13 +46,13 @@ int test__openat_syscall_event(struct test *test __maybe_unused, int subtest __m
                close(fd);
        }
 
-       if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
-               pr_debug("perf_evsel__read_on_cpu\n");
+       if (evsel__read_on_cpu(evsel, 0, 0) < 0) {
+               pr_debug("evsel__read_on_cpu\n");
                goto out_close_fd;
        }
 
        if (perf_counts(evsel->counts, 0, 0)->val != nr_openat_calls) {
-               pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
+               pr_debug("evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
                         nr_openat_calls, perf_counts(evsel->counts, 0, 0)->val);
                goto out_close_fd;
        }
index 091c3aeccc2798a1c32f08069ccd06b32d4154bb..895188b63f9635b2c32ac2f241a9e14aa6aa3c92 100644 (file)
@@ -371,7 +371,7 @@ static int test__checkevent_breakpoint_modifier(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "mem:0:u"));
+                       !strcmp(evsel__name(evsel), "mem:0:u"));
 
        return test__checkevent_breakpoint(evlist);
 }
@@ -385,7 +385,7 @@ static int test__checkevent_breakpoint_x_modifier(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "mem:0:x:k"));
+                       !strcmp(evsel__name(evsel), "mem:0:x:k"));
 
        return test__checkevent_breakpoint_x(evlist);
 }
@@ -399,7 +399,7 @@ static int test__checkevent_breakpoint_r_modifier(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", !evsel->core.attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "mem:0:r:hp"));
+                       !strcmp(evsel__name(evsel), "mem:0:r:hp"));
 
        return test__checkevent_breakpoint_r(evlist);
 }
@@ -413,7 +413,7 @@ static int test__checkevent_breakpoint_w_modifier(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "mem:0:w:up"));
+                       !strcmp(evsel__name(evsel), "mem:0:w:up"));
 
        return test__checkevent_breakpoint_w(evlist);
 }
@@ -427,7 +427,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", evsel->core.attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "mem:0:rw:kp"));
+                       !strcmp(evsel__name(evsel), "mem:0:rw:kp"));
 
        return test__checkevent_breakpoint_rw(evlist);
 }
@@ -468,7 +468,7 @@ static int test__checkevent_list(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
 
        /* syscalls:sys_enter_openat:k */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong sample_type",
                PERF_TP_SAMPLE_TYPE == evsel->core.attr.sample_type);
@@ -479,7 +479,7 @@ static int test__checkevent_list(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
 
        /* 1:1:hp */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config", 1 == evsel->core.attr.config);
        TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
@@ -498,15 +498,15 @@ static int test__checkevent_pmu_name(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",  1 == evsel->core.attr.config);
-       TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
+       TEST_ASSERT_VAL("wrong name", !strcmp(evsel__name(evsel), "krava"));
 
        /* cpu/config=2/u" */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",  2 == evsel->core.attr.config);
        TEST_ASSERT_VAL("wrong name",
-                       !strcmp(perf_evsel__name(evsel), "cpu/config=2/u"));
+                       !strcmp(evsel__name(evsel), "cpu/config=2/u"));
 
        return 0;
 }
@@ -529,7 +529,7 @@ static int test__checkevent_pmu_partial_time_callgraph(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong time",  !(PERF_SAMPLE_TIME & evsel->core.attr.sample_type));
 
        /* cpu/config=2,call-graph=no,time=0,period=2000/ */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",  2 == evsel->core.attr.config);
        /*
@@ -577,7 +577,7 @@ static int test__checkevent_pmu_events_mix(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
 
        /* cpu/pmu-event/u*/
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong exclude_user",
@@ -652,13 +652,13 @@ static int test__group1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:upp */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -670,7 +670,7 @@ static int test__group1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
@@ -694,13 +694,13 @@ static int test__group2(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cache-references + :u modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_REFERENCES == evsel->core.attr.config);
@@ -711,11 +711,11 @@ static int test__group2(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:k */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -725,7 +725,7 @@ static int test__group2(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
@@ -750,15 +750,15 @@ static int test__group3(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong group name",
                !strcmp(leader->group_name, "group1"));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group1 cycles:kppp */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -771,11 +771,11 @@ static int test__group3(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 3);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group2 cycles + G modifier */
-       evsel = leader = perf_evsel__next(evsel);
+       evsel = leader = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -785,15 +785,15 @@ static int test__group3(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong group name",
                !strcmp(leader->group_name, "group2"));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group2 1:3 + G modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", 1 == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config", 3 == evsel->core.attr.config);
        TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
@@ -803,11 +803,11 @@ static int test__group3(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:u */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
@@ -817,7 +817,7 @@ static int test__group3(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude guest", !evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
@@ -843,13 +843,13 @@ static int test__group4(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 1);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:kp + p */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
@@ -861,7 +861,7 @@ static int test__group4(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", evsel->core.attr.precise_ip == 2);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
@@ -886,13 +886,13 @@ static int test__group5(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions + G */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
@@ -903,11 +903,11 @@ static int test__group5(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:G */
-       evsel = leader = perf_evsel__next(evsel);
+       evsel = leader = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -918,13 +918,13 @@ static int test__group5(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
        TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:G */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_INSTRUCTIONS == evsel->core.attr.config);
@@ -935,10 +935,10 @@ static int test__group5(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
 
        /* cycles */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CPU_CYCLES == evsel->core.attr.config);
@@ -948,7 +948,7 @@ static int test__group5(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude guest", evsel->core.attr.exclude_guest);
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
 
        return 0;
 }
@@ -972,12 +972,12 @@ static int test__group_gh1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
 
        /* cache-misses:G + :H group modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
@@ -988,7 +988,7 @@ static int test__group_gh1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
 
        return 0;
 }
@@ -1012,12 +1012,12 @@ static int test__group_gh2(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
 
        /* cache-misses:H + :G group modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
@@ -1028,7 +1028,7 @@ static int test__group_gh2(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
 
        return 0;
 }
@@ -1052,12 +1052,12 @@ static int test__group_gh3(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
 
        /* cache-misses:H + :u group modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
@@ -1068,7 +1068,7 @@ static int test__group_gh3(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
 
        return 0;
 }
@@ -1092,12 +1092,12 @@ static int test__group_gh4(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
-       TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong leader", evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong core.nr_members", evsel->core.nr_members == 2);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 0);
 
        /* cache-misses:H + :uG group modifier */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
@@ -1108,7 +1108,7 @@ static int test__group_gh4(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->core.attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->core.attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
-       TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong group_idx", evsel__group_idx(evsel) == 1);
 
        return 0;
 }
@@ -1135,7 +1135,7 @@ static int test__leader_sample1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
 
        /* cache-misses - not sampling */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
@@ -1149,7 +1149,7 @@ static int test__leader_sample1(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
 
        /* branch-misses - not sampling */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
@@ -1188,7 +1188,7 @@ static int test__leader_sample2(struct evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
 
        /* branch-misses - not sampling */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
@@ -1234,14 +1234,14 @@ static int test__pinned_group(struct evlist *evlist)
        TEST_ASSERT_VAL("wrong pinned", evsel->core.attr.pinned);
 
        /* cache-misses - can not be pinned, but will go on with the leader */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_CACHE_MISSES == evsel->core.attr.config);
        TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
 
        /* branch-misses - ditto */
-       evsel = perf_evsel__next(evsel);
+       evsel = evsel__next(evsel);
        TEST_ASSERT_VAL("wrong config",
                        PERF_COUNT_HW_BRANCH_MISSES == evsel->core.attr.config);
        TEST_ASSERT_VAL("wrong pinned", !evsel->core.attr.pinned);
@@ -1356,6 +1356,16 @@ static int test__checkevent_complex_name(struct evlist *evlist)
        return 0;
 }
 
+static int test__checkevent_raw_pmu(struct evlist *evlist)
+{
+       struct evsel *evsel = evlist__first(evlist);
+
+       TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
+       TEST_ASSERT_VAL("wrong config", 0x1a == evsel->core.attr.config);
+       return 0;
+}
+
 static int test__sym_event_slash(struct evlist *evlist)
 {
        struct evsel *evsel = evlist__first(evlist);
@@ -1750,7 +1760,12 @@ static struct evlist_test test__events_pmu[] = {
                .name  = "cpu/name='COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks',period=0x1,event=0x2/ukp",
                .check = test__checkevent_complex_name,
                .id    = 3,
-       }
+       },
+       {
+               .name  = "software/r1a/",
+               .check = test__checkevent_raw_pmu,
+               .id    = 4,
+       },
 };
 
 struct terms_test {
index 2195fc205e72b672770905d40a3ac0a0b8dfb293..83adfd846ccda5c1b83ca6563fda4c4bb65506d1 100644 (file)
@@ -106,9 +106,9 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
         * Config the evsels, setting attr->comm on the first one, etc.
         */
        evsel = evlist__first(evlist);
-       perf_evsel__set_sample_bit(evsel, CPU);
-       perf_evsel__set_sample_bit(evsel, TID);
-       perf_evsel__set_sample_bit(evsel, TIME);
+       evsel__set_sample_bit(evsel, CPU);
+       evsel__set_sample_bit(evsel, TID);
+       evsel__set_sample_bit(evsel, TIME);
        perf_evlist__config(evlist, &opts, NULL);
 
        err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
index 61865699c3f4fc62dc8090bbd126e1cdcab1eba6..a0bdaf390ac8ea78938beb52d2171b9018a1fbd7 100644 (file)
@@ -296,12 +296,12 @@ static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
                goto out_free;
        }
 
-       evsel.sample_size = __perf_evsel__sample_size(sample_type);
+       evsel.sample_size = __evsel__sample_size(sample_type);
 
-       err = perf_evsel__parse_sample(&evsel, event, &sample_out);
+       err = evsel__parse_sample(&evsel, event, &sample_out);
        if (err) {
                pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
-                        "perf_evsel__parse_sample", sample_type, err);
+                        "evsel__parse_sample", sample_type, err);
                goto out_free;
        }
 
index fcb0d03dba4edea6f96dd615c66a63b1d98b3d9c..db5e1f70053a87057473a535d57c86b3f556a837 100644 (file)
@@ -135,8 +135,8 @@ static int process_sample_event(struct evlist *evlist,
 
        evsel = perf_evlist__id2evsel(evlist, sample.id);
        if (evsel == switch_tracking->switch_evsel) {
-               next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
-               prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
+               next_tid = evsel__intval(evsel, &sample, "next_pid");
+               prev_tid = evsel__intval(evsel, &sample, "prev_pid");
                cpu = sample.cpu;
                pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
                          cpu, prev_tid, next_tid);
@@ -394,8 +394,8 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
 
        switch_evsel = evlist__last(evlist);
 
-       perf_evsel__set_sample_bit(switch_evsel, CPU);
-       perf_evsel__set_sample_bit(switch_evsel, TIME);
+       evsel__set_sample_bit(switch_evsel, CPU);
+       evsel__set_sample_bit(switch_evsel, TIME);
 
        switch_evsel->core.system_wide = true;
        switch_evsel->no_aux_samples = true;
@@ -412,8 +412,8 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
                goto out_err;
        }
 
-       perf_evsel__set_sample_bit(cycles_evsel, CPU);
-       perf_evsel__set_sample_bit(cycles_evsel, TIME);
+       evsel__set_sample_bit(cycles_evsel, CPU);
+       evsel__set_sample_bit(cycles_evsel, TIME);
 
        /* Fourth event */
        err = parse_events(evlist, "dummy:u", NULL);
@@ -429,7 +429,7 @@ int test__switch_tracking(struct test *test __maybe_unused, int subtest __maybe_
        tracking_evsel->core.attr.freq = 0;
        tracking_evsel->core.attr.sample_period = 1;
 
-       perf_evsel__set_sample_bit(tracking_evsel, TIME);
+       evsel__set_sample_bit(tracking_evsel, TIME);
 
        /* Config events */
        perf_evlist__config(evlist, &opts, NULL);
index 61a1ab032080dc5e041b9a8168ea5d12e88c3f6f..d6d4ac34eeb7eb9ae909abf37ac628e6589f768e 100644 (file)
@@ -112,6 +112,7 @@ int test__mem2node(struct test *t, int subtest);
 int test__maps__merge_in(struct test *t, int subtest);
 int test__time_utils(struct test *t, int subtest);
 int test__jit_write_elf(struct test *test, int subtest);
+int test__api_io(struct test *test, int subtest);
 
 bool test__bp_signal_is_supported(void);
 bool test__bp_account_is_supported(void);
index 4a800499d7c35fbf7337292f5d615a86c26ad382..22daf2bdf5faf1b0e7a5ac85ec021c4af80b4371 100644 (file)
@@ -33,10 +33,8 @@ static int session_write_header(char *path)
 {
        struct perf_session *session;
        struct perf_data data = {
-               .file      = {
-                       .path = path,
-               },
-               .mode      = PERF_DATA_MODE_WRITE,
+               .path = path,
+               .mode = PERF_DATA_MODE_WRITE,
        };
 
        session = perf_session__new(&data, false, NULL);
@@ -63,10 +61,8 @@ static int check_cpu_topology(char *path, struct perf_cpu_map *map)
 {
        struct perf_session *session;
        struct perf_data data = {
-               .file      = {
-                       .path = path,
-               },
-               .mode      = PERF_DATA_MODE_READ,
+               .path = path,
+               .mode = PERF_DATA_MODE_READ,
        };
        int i;
 
index 487e54ef56a981bc7600d2a0ca905e7a9106b27f..f98a118dfc49eb4142d555f1ab477f098c315430 100644 (file)
@@ -3416,7 +3416,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
        struct hists *hists = evsel__hists(evsel);
        bool current_entry = ui_browser__is_current_entry(browser, row);
        unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
-       const char *ev_name = perf_evsel__name(evsel);
+       const char *ev_name = evsel__name(evsel);
        char bf[256], unit;
        const char *warn = " ";
        size_t printed;
@@ -3424,10 +3424,10 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
        ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
                                                       HE_COLORSET_NORMAL);
 
-       if (perf_evsel__is_group_event(evsel)) {
+       if (evsel__is_group_event(evsel)) {
                struct evsel *pos;
 
-               ev_name = perf_evsel__group_name(evsel);
+               ev_name = evsel__group_name(evsel);
 
                for_each_group_member(pos, evsel) {
                        struct hists *pos_hists = evsel__hists(pos);
@@ -3512,13 +3512,13 @@ browse_hists:
                                if (pos->core.node.next == &evlist->core.entries)
                                        pos = evlist__first(evlist);
                                else
-                                       pos = perf_evsel__next(pos);
+                                       pos = evsel__next(pos);
                                goto browse_hists;
                        case K_UNTAB:
                                if (pos->core.node.prev == &evlist->core.entries)
                                        pos = evlist__last(evlist);
                                else
-                                       pos = perf_evsel__prev(pos);
+                                       pos = evsel__prev(pos);
                                goto browse_hists;
                        case K_SWITCH_INPUT_DATA:
                        case K_RELOAD:
@@ -3554,7 +3554,7 @@ static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
 {
        struct evsel *evsel = list_entry(entry, struct evsel, core.node);
 
-       if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
+       if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
                return true;
 
        return false;
@@ -3587,7 +3587,7 @@ static int __perf_evlist__tui_browse_hists(struct evlist *evlist,
        ui_helpline__push("Press ESC to exit");
 
        evlist__for_each_entry(evlist, pos) {
-               const char *ev_name = perf_evsel__name(pos);
+               const char *ev_name = evsel__name(pos);
                size_t line_len = strlen(ev_name) + 7;
 
                if (menu.b.width < line_len)
@@ -3622,7 +3622,7 @@ single_entry:
 
                nr_entries = 0;
                evlist__for_each_entry(evlist, pos) {
-                       if (perf_evsel__is_group_leader(pos))
+                       if (evsel__is_group_leader(pos))
                                nr_entries++;
                }
 
@@ -3640,7 +3640,7 @@ static int block_hists_browser__title(struct hist_browser *browser, char *bf,
                                      size_t size)
 {
        struct hists *hists = evsel__hists(browser->block_evsel);
-       const char *evname = perf_evsel__name(browser->block_evsel);
+       const char *evname = evsel__name(browser->block_evsel);
        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
        int ret;
 
index 35f9641bf670cb5bc9103efffa8f060e9d8d4627..a7dff77f20184f1fdade65a9535733b498383c94 100644 (file)
@@ -130,7 +130,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct map_symbol *ms,
 
                gtk_list_store_append(store, &iter);
 
-               if (perf_evsel__is_group_event(evsel)) {
+               if (evsel__is_group_event(evsel)) {
                        for (i = 0; i < evsel->core.nr_members; i++) {
                                ret += perf_gtk__get_percent(s + ret,
                                                             sizeof(s) - ret,
index ed1a97b2c4b01de690d6bd2044a9bdae14d2fdd3..53ef71a1b15dc95ce39227ef77eaaa8b6d0d0867 100644 (file)
@@ -635,18 +635,18 @@ int perf_evlist__gtk_browse_hists(struct evlist *evlist,
 
        evlist__for_each_entry(evlist, pos) {
                struct hists *hists = evsel__hists(pos);
-               const char *evname = perf_evsel__name(pos);
+               const char *evname = evsel__name(pos);
                GtkWidget *scrolled_window;
                GtkWidget *tab_label;
                char buf[512];
                size_t size = sizeof(buf);
 
                if (symbol_conf.event_group) {
-                       if (!perf_evsel__is_group_leader(pos))
+                       if (!evsel__is_group_leader(pos))
                                continue;
 
                        if (pos->core.nr_members > 1) {
-                               perf_evsel__group_desc(pos, buf, size);
+                               evsel__group_desc(pos, buf, size);
                                evname = buf;
                        }
                }
index 025f4c7f96bfb95d5cf0fc5e1ae28f5490e14027..c1f24d00485272bbbd0a87bc9762d6b304b1a349 100644 (file)
@@ -43,12 +43,12 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
        } else
                ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
 
-       if (perf_evsel__is_group_event(evsel)) {
+       if (evsel__is_group_event(evsel)) {
                int prev_idx, idx_delta;
                struct hist_entry *pair;
                int nr_members = evsel->core.nr_members;
 
-               prev_idx = perf_evsel__group_idx(evsel);
+               prev_idx = evsel__group_idx(evsel);
 
                list_for_each_entry(pair, &he->pairs.head, pairs.node) {
                        u64 period = get_field(pair);
@@ -58,7 +58,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
                                continue;
 
                        evsel = hists_to_evsel(pair->hists);
-                       idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
+                       idx_delta = evsel__group_idx(evsel) - prev_idx - 1;
 
                        while (idx_delta--) {
                                /*
@@ -82,7 +82,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
                                                          len, period);
                        }
 
-                       prev_idx = perf_evsel__group_idx(evsel);
+                       prev_idx = evsel__group_idx(evsel);
                }
 
                idx_delta = nr_members - prev_idx - 1;
@@ -164,12 +164,12 @@ static int hist_entry__new_pair(struct hist_entry *a, struct hist_entry *b,
 
        list_for_each_entry(pair, &a->pairs.head, pairs.node) {
                struct evsel *evsel = hists_to_evsel(pair->hists);
-               fa[perf_evsel__group_idx(evsel)] = get_field(pair);
+               fa[evsel__group_idx(evsel)] = get_field(pair);
        }
 
        list_for_each_entry(pair, &b->pairs.head, pairs.node) {
                struct evsel *evsel = hists_to_evsel(pair->hists);
-               fb[perf_evsel__group_idx(evsel)] = get_field(pair);
+               fb[evsel__group_idx(evsel)] = get_field(pair);
        }
 
        *fields_a = fa;
@@ -190,7 +190,7 @@ static int __hpp__group_sort_idx(struct hist_entry *a, struct hist_entry *b,
        int cmp, nr_members, ret, i;
 
        cmp = field_cmp(get_field(a), get_field(b));
-       if (!perf_evsel__is_group_event(evsel))
+       if (!evsel__is_group_event(evsel))
                return cmp;
 
        nr_members = evsel->core.nr_members;
@@ -240,7 +240,7 @@ static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
                return ret;
 
        evsel = hists_to_evsel(a->hists);
-       if (!perf_evsel__is_group_event(evsel))
+       if (!evsel__is_group_event(evsel))
                return ret;
 
        nr_members = evsel->core.nr_members;
index c0cf8dff694eb19be2a7140a0cd362b4189a84de..ca07a162d6028a0ddc48219f2142f44a65f55d7a 100644 (file)
@@ -10,6 +10,7 @@ perf-y += db-export.o
 perf-y += env.o
 perf-y += event.o
 perf-y += evlist.o
+perf-y += sideband_evlist.o
 perf-y += evsel.o
 perf-y += evsel_fprintf.o
 perf-y += perf_event_attr_fprintf.o
@@ -88,6 +89,7 @@ perf-y += counts.o
 perf-y += stat.o
 perf-y += stat-shadow.o
 perf-y += stat-display.o
+perf-y += perf_api_probe.o
 perf-y += record.o
 perf-y += srcline.o
 perf-y += srccode.o
index f1ea0d61eb5b2a33b184f64063ed38be8ede8e98..d828c2d2edeead9747a6b19ffa5f205e4cf98d99 100644 (file)
@@ -1191,7 +1191,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args)
        struct disasm_line *dl = NULL;
        int nr = 1;
 
-       if (perf_evsel__is_group_event(args->evsel))
+       if (evsel__is_group_event(args->evsel))
                nr = args->evsel->core.nr_members;
 
        dl = zalloc(disasm_line_size(nr));
@@ -1437,7 +1437,7 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start
                if (queue)
                        return -1;
 
-               if (perf_evsel__is_group_event(evsel))
+               if (evsel__is_group_event(evsel))
                        width *= evsel->core.nr_members;
 
                if (!*al->line)
@@ -1821,6 +1821,24 @@ static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused,
 }
 #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
 
+static int
+symbol__disassemble_bpf_image(struct symbol *sym,
+                             struct annotate_args *args)
+{
+       struct annotation *notes = symbol__annotation(sym);
+       struct disasm_line *dl;
+
+       args->offset = -1;
+       args->line = strdup("to be implemented");
+       args->line_nr = 0;
+       dl = disasm_line__new(args);
+       if (dl)
+               annotation_line__add(&dl->al, &notes->src->source);
+
+       free(args->line);
+       return 0;
+}
+
 /*
  * Possibly create a new version of line with tabs expanded. Returns the
  * existing or new line, storage is updated if a new line is allocated. If
@@ -1920,6 +1938,8 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
 
        if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) {
                return symbol__disassemble_bpf(sym, args);
+       } else if (dso->binary_type == DSO_BINARY_TYPE__BPF_IMAGE) {
+               return symbol__disassemble_bpf_image(sym, args);
        } else if (dso__is_kcore(dso)) {
                kce.kcore_filename = symfs_filename;
                kce.addr = map__rip_2objdump(map, sym->start);
@@ -2136,7 +2156,7 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel,
                .evsel          = evsel,
                .options        = options,
        };
-       struct perf_env *env = perf_evsel__env(evsel);
+       struct perf_env *env = evsel__env(evsel);
        const char *arch_name = perf_env__arch(env);
        struct arch *arch;
        int err;
@@ -2324,7 +2344,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,
        struct dso *dso = map->dso;
        char *filename;
        const char *d_filename;
-       const char *evsel_name = perf_evsel__name(evsel);
+       const char *evsel_name = evsel__name(evsel);
        struct annotation *notes = symbol__annotation(sym);
        struct sym_hist *h = annotation__histogram(notes, evsel->idx);
        struct annotation_line *pos, *queue = NULL;
@@ -2348,9 +2368,9 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,
 
        len = symbol__size(sym);
 
-       if (perf_evsel__is_group_event(evsel)) {
+       if (evsel__is_group_event(evsel)) {
                width *= evsel->core.nr_members;
-               perf_evsel__group_desc(evsel, buf, sizeof(buf));
+               evsel__group_desc(evsel, buf, sizeof(buf));
                evsel_name = buf;
        }
 
@@ -2485,7 +2505,7 @@ static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp,
 int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
                                struct annotation_options *opts)
 {
-       const char *ev_name = perf_evsel__name(evsel);
+       const char *ev_name = evsel__name(evsel);
        char buf[1024];
        char *filename;
        int err = -1;
@@ -2498,8 +2518,8 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
        if (fp == NULL)
                goto out_free_filename;
 
-       if (perf_evsel__is_group_event(evsel)) {
-               perf_evsel__group_desc(evsel, buf, sizeof(buf));
+       if (evsel__is_group_event(evsel)) {
+               evsel__group_desc(evsel, buf, sizeof(buf));
                ev_name = buf;
        }
 
@@ -3044,7 +3064,7 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel,
        if (notes->offsets == NULL)
                return ENOMEM;
 
-       if (perf_evsel__is_group_event(evsel))
+       if (evsel__is_group_event(evsel))
                nr_pcnt = evsel->core.nr_members;
 
        err = symbol__annotate(ms, evsel, options, parch);
index 53be12b23ff406cc74d67e36f9f311e3b84f732b..875a0dd540e53777082c35ad8b3dc7759c483a47 100644 (file)
@@ -176,6 +176,14 @@ static void arm_spe_free(struct perf_session *session)
        free(spe);
 }
 
+static bool arm_spe_evsel_is_auxtrace(struct perf_session *session,
+                                     struct evsel *evsel)
+{
+       struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, auxtrace);
+
+       return evsel->core.attr.type == spe->pmu_type;
+}
+
 static const char * const arm_spe_info_fmts[] = {
        [ARM_SPE_PMU_TYPE]              = "  PMU Type           %"PRId64"\n",
 };
@@ -218,6 +226,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event,
        spe->auxtrace.flush_events = arm_spe_flush;
        spe->auxtrace.free_events = arm_spe_free_events;
        spe->auxtrace.free = arm_spe_free;
+       spe->auxtrace.evsel_is_auxtrace = arm_spe_evsel_is_auxtrace;
        session->auxtrace = &spe->auxtrace;
 
        arm_spe_print_info(&auxtrace_info->priv[0]);
index 3571ce72ca28e7e6ee68adcab72ef3398d88350d..749487a41cc789c9128e34ec2324678b7c65af6f 100644 (file)
@@ -33,6 +33,7 @@
 #include "evsel.h"
 #include "evsel_config.h"
 #include "symbol.h"
+#include "util/perf_api_probe.h"
 #include "util/synthetic-events.h"
 #include "thread_map.h"
 #include "asm/bug.h"
 #include "symbol/kallsyms.h"
 #include <internal/lib.h>
 
-static struct perf_pmu *perf_evsel__find_pmu(struct evsel *evsel)
-{
-       struct perf_pmu *pmu = NULL;
-
-       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
-               if (pmu->type == evsel->core.attr.type)
-                       break;
-       }
-
-       return pmu;
-}
-
-static bool perf_evsel__is_aux_event(struct evsel *evsel)
-{
-       struct perf_pmu *pmu = perf_evsel__find_pmu(evsel);
-
-       return pmu && pmu->auxtrace;
-}
-
 /*
  * Make a group from 'leader' to 'last', requiring that the events were not
  * already grouped to a different leader.
@@ -88,7 +70,7 @@ static int perf_evlist__regroup(struct evlist *evlist,
        struct evsel *evsel;
        bool grp;
 
-       if (!perf_evsel__is_group_leader(leader))
+       if (!evsel__is_group_leader(leader))
                return -EINVAL;
 
        grp = false;
@@ -703,8 +685,8 @@ static int auxtrace_validate_aux_sample_size(struct evlist *evlist,
 
        evlist__for_each_entry(evlist, evsel) {
                sz = evsel->core.attr.aux_sample_size;
-               if (perf_evsel__is_group_leader(evsel)) {
-                       has_aux_leader = perf_evsel__is_aux_event(evsel);
+               if (evsel__is_group_leader(evsel)) {
+                       has_aux_leader = evsel__is_aux_event(evsel);
                        if (sz) {
                                if (has_aux_leader)
                                        pr_err("Cannot add AUX area sampling to an AUX area event\n");
@@ -723,10 +705,10 @@ static int auxtrace_validate_aux_sample_size(struct evlist *evlist,
                                pr_err("Cannot add AUX area sampling because group leader is not an AUX area event\n");
                                return -EINVAL;
                        }
-                       perf_evsel__set_sample_bit(evsel, AUX);
+                       evsel__set_sample_bit(evsel, AUX);
                        opts->auxtrace_sample_mode = true;
                } else {
-                       perf_evsel__reset_sample_bit(evsel, AUX);
+                       evsel__reset_sample_bit(evsel, AUX);
                }
        }
 
@@ -777,8 +759,8 @@ int auxtrace_parse_sample_options(struct auxtrace_record *itr,
 
        /* Set aux_sample_size based on --aux-sample option */
        evlist__for_each_entry(evlist, evsel) {
-               if (perf_evsel__is_group_leader(evsel)) {
-                       has_aux_leader = perf_evsel__is_aux_event(evsel);
+               if (evsel__is_group_leader(evsel)) {
+                       has_aux_leader = evsel__is_aux_event(evsel);
                } else if (has_aux_leader) {
                        evsel->core.attr.aux_sample_size = sz;
                }
@@ -787,7 +769,7 @@ no_opt:
        aux_evsel = NULL;
        /* Override with aux_sample_size from config term */
        evlist__for_each_entry(evlist, evsel) {
-               if (perf_evsel__is_aux_event(evsel))
+               if (evsel__is_aux_event(evsel))
                        aux_evsel = evsel;
                term = perf_evsel__get_config_term(evsel, AUX_SAMPLE_SIZE);
                if (term) {
@@ -1234,29 +1216,79 @@ out_free:
        return err;
 }
 
+static void unleader_evsel(struct evlist *evlist, struct evsel *leader)
+{
+       struct evsel *new_leader = NULL;
+       struct evsel *evsel;
+
+       /* Find new leader for the group */
+       evlist__for_each_entry(evlist, evsel) {
+               if (evsel->leader != leader || evsel == leader)
+                       continue;
+               if (!new_leader)
+                       new_leader = evsel;
+               evsel->leader = new_leader;
+       }
+
+       /* Update group information */
+       if (new_leader) {
+               zfree(&new_leader->group_name);
+               new_leader->group_name = leader->group_name;
+               leader->group_name = NULL;
+
+               new_leader->core.nr_members = leader->core.nr_members - 1;
+               leader->core.nr_members = 1;
+       }
+}
+
+static void unleader_auxtrace(struct perf_session *session)
+{
+       struct evsel *evsel;
+
+       evlist__for_each_entry(session->evlist, evsel) {
+               if (auxtrace__evsel_is_auxtrace(session, evsel) &&
+                   evsel__is_group_leader(evsel)) {
+                       unleader_evsel(session->evlist, evsel);
+               }
+       }
+}
+
 int perf_event__process_auxtrace_info(struct perf_session *session,
                                      union perf_event *event)
 {
        enum auxtrace_type type = event->auxtrace_info.type;
+       int err;
 
        if (dump_trace)
                fprintf(stdout, " type: %u\n", type);
 
        switch (type) {
        case PERF_AUXTRACE_INTEL_PT:
-               return intel_pt_process_auxtrace_info(event, session);
+               err = intel_pt_process_auxtrace_info(event, session);
+               break;
        case PERF_AUXTRACE_INTEL_BTS:
-               return intel_bts_process_auxtrace_info(event, session);
+               err = intel_bts_process_auxtrace_info(event, session);
+               break;
        case PERF_AUXTRACE_ARM_SPE:
-               return arm_spe_process_auxtrace_info(event, session);
+               err = arm_spe_process_auxtrace_info(event, session);
+               break;
        case PERF_AUXTRACE_CS_ETM:
-               return cs_etm__process_auxtrace_info(event, session);
+               err = cs_etm__process_auxtrace_info(event, session);
+               break;
        case PERF_AUXTRACE_S390_CPUMSF:
-               return s390_cpumsf_process_auxtrace_info(event, session);
+               err = s390_cpumsf_process_auxtrace_info(event, session);
+               break;
        case PERF_AUXTRACE_UNKNOWN:
        default:
                return -EINVAL;
        }
+
+       if (err)
+               return err;
+
+       unleader_auxtrace(session);
+
+       return 0;
 }
 
 s64 perf_event__process_auxtrace(struct perf_session *session,
@@ -1412,8 +1444,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
                        synth_opts->branches = true;
                        synth_opts->returns = true;
                        break;
+               case 'G':
                case 'g':
-                       synth_opts->callchain = true;
+                       if (p[-1] == 'G')
+                               synth_opts->add_callchain = true;
+                       else
+                               synth_opts->callchain = true;
                        synth_opts->callchain_sz =
                                        PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
                        while (*p == ' ' || *p == ',')
@@ -1428,8 +1464,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
                                synth_opts->callchain_sz = val;
                        }
                        break;
+               case 'L':
                case 'l':
-                       synth_opts->last_branch = true;
+                       if (p[-1] == 'L')
+                               synth_opts->add_last_branch = true;
+                       else
+                               synth_opts->last_branch = true;
                        synth_opts->last_branch_sz =
                                        PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
                        while (*p == ' ' || *p == ',')
@@ -2482,7 +2522,7 @@ static int parse_addr_filter(struct evsel *evsel, const char *filter,
                        goto out_exit;
                }
 
-               if (perf_evsel__append_addr_filter(evsel, new_filter)) {
+               if (evsel__append_addr_filter(evsel, new_filter)) {
                        err = -ENOMEM;
                        goto out_exit;
                }
@@ -2500,9 +2540,9 @@ out_exit:
        return err;
 }
 
-static int perf_evsel__nr_addr_filter(struct evsel *evsel)
+static int evsel__nr_addr_filter(struct evsel *evsel)
 {
-       struct perf_pmu *pmu = perf_evsel__find_pmu(evsel);
+       struct perf_pmu *pmu = evsel__find_pmu(evsel);
        int nr_addr_filters = 0;
 
        if (!pmu)
@@ -2521,7 +2561,7 @@ int auxtrace_parse_filters(struct evlist *evlist)
 
        evlist__for_each_entry(evlist, evsel) {
                filter = evsel->filter;
-               max_nr = perf_evsel__nr_addr_filter(evsel);
+               max_nr = evsel__nr_addr_filter(evsel);
                if (!filter || !max_nr)
                        continue;
                evsel->filter = NULL;
@@ -2577,3 +2617,12 @@ void auxtrace__free(struct perf_session *session)
 
        return session->auxtrace->free(session);
 }
+
+bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
+                                struct evsel *evsel)
+{
+       if (!session->auxtrace || !session->auxtrace->evsel_is_auxtrace)
+               return false;
+
+       return session->auxtrace->evsel_is_auxtrace(session, evsel);
+}
index e58ef160b59992602fd89c6c48260b3ceafd5890..0220a2e86c164889ec2238b4f575dce747a1a111 100644 (file)
@@ -21,6 +21,7 @@
 union perf_event;
 struct perf_session;
 struct evlist;
+struct evsel;
 struct perf_tool;
 struct mmap;
 struct perf_sample;
@@ -73,8 +74,10 @@ enum itrace_period_type {
  * @calls: limit branch samples to calls (can be combined with @returns)
  * @returns: limit branch samples to returns (can be combined with @calls)
  * @callchain: add callchain to 'instructions' events
+ * @add_callchain: add callchain to existing event records
  * @thread_stack: feed branches to the thread_stack
  * @last_branch: add branch context to 'instruction' events
+ * @add_last_branch: add branch context to existing event records
  * @callchain_sz: maximum callchain size
  * @last_branch_sz: branch context size
  * @period: 'instructions' events period
@@ -100,8 +103,10 @@ struct itrace_synth_opts {
        bool                    calls;
        bool                    returns;
        bool                    callchain;
+       bool                    add_callchain;
        bool                    thread_stack;
        bool                    last_branch;
+       bool                    add_last_branch;
        unsigned int            callchain_sz;
        unsigned int            last_branch_sz;
        unsigned long long      period;
@@ -166,6 +171,8 @@ struct auxtrace {
                            struct perf_tool *tool);
        void (*free_events)(struct perf_session *session);
        void (*free)(struct perf_session *session);
+       bool (*evsel_is_auxtrace)(struct perf_session *session,
+                                 struct evsel *evsel);
 };
 
 /**
@@ -584,6 +591,8 @@ void auxtrace__dump_auxtrace_sample(struct perf_session *session,
 int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool);
 void auxtrace__free_events(struct perf_session *session);
 void auxtrace__free(struct perf_session *session);
+bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
+                                struct evsel *evsel);
 
 #define ITRACE_HELP \
 "                              i:                      synthesize instructions events\n"               \
@@ -749,6 +758,13 @@ void auxtrace_index__free(struct list_head *head __maybe_unused)
 {
 }
 
+static inline
+bool auxtrace__evsel_is_auxtrace(struct perf_session *session __maybe_unused,
+                                struct evsel *evsel __maybe_unused)
+{
+       return false;
+}
+
 static inline
 int auxtrace_parse_filters(struct evlist *evlist __maybe_unused)
 {
index a3207d9003391aa4d342d3998a9f00b307aafc47..3742511a08d15cc942e041a45caebc32eb5dd1b2 100644 (file)
@@ -6,6 +6,9 @@
 #include <bpf/libbpf.h>
 #include <linux/btf.h>
 #include <linux/err.h>
+#include <linux/string.h>
+#include <internal/lib.h>
+#include <symbol/kallsyms.h>
 #include "bpf-event.h"
 #include "debug.h"
 #include "dso.h"
@@ -290,11 +293,82 @@ out:
        return err ? -1 : 0;
 }
 
+struct kallsyms_parse {
+       union perf_event        *event;
+       perf_event__handler_t    process;
+       struct machine          *machine;
+       struct perf_tool        *tool;
+};
+
+static int
+process_bpf_image(char *name, u64 addr, struct kallsyms_parse *data)
+{
+       struct machine *machine = data->machine;
+       union perf_event *event = data->event;
+       struct perf_record_ksymbol *ksymbol;
+       int len;
+
+       ksymbol = &event->ksymbol;
+
+       *ksymbol = (struct perf_record_ksymbol) {
+               .header = {
+                       .type = PERF_RECORD_KSYMBOL,
+                       .size = offsetof(struct perf_record_ksymbol, name),
+               },
+               .addr      = addr,
+               .len       = page_size,
+               .ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
+               .flags     = 0,
+       };
+
+       len = scnprintf(ksymbol->name, KSYM_NAME_LEN, "%s", name);
+       ksymbol->header.size += PERF_ALIGN(len + 1, sizeof(u64));
+       memset((void *) event + event->header.size, 0, machine->id_hdr_size);
+       event->header.size += machine->id_hdr_size;
+
+       return perf_tool__process_synth_event(data->tool, event, machine,
+                                             data->process);
+}
+
+static int
+kallsyms_process_symbol(void *data, const char *_name,
+                       char type __maybe_unused, u64 start)
+{
+       char disp[KSYM_NAME_LEN];
+       char *module, *name;
+       unsigned long id;
+       int err = 0;
+
+       module = strchr(_name, '\t');
+       if (!module)
+               return 0;
+
+       /* We are going after [bpf] module ... */
+       if (strcmp(module + 1, "[bpf]"))
+               return 0;
+
+       name = memdup(_name, (module - _name) + 1);
+       if (!name)
+               return -ENOMEM;
+
+       name[module - _name] = 0;
+
+       /* .. and only for trampolines and dispatchers */
+       if ((sscanf(name, "bpf_trampoline_%lu", &id) == 1) ||
+           (sscanf(name, "bpf_dispatcher_%s", disp) == 1))
+               err = process_bpf_image(name, start, data);
+
+       free(name);
+       return err;
+}
+
 int perf_event__synthesize_bpf_events(struct perf_session *session,
                                      perf_event__handler_t process,
                                      struct machine *machine,
                                      struct record_opts *opts)
 {
+       const char *kallsyms_filename = "/proc/kallsyms";
+       struct kallsyms_parse arg;
        union perf_event *event;
        __u32 id = 0;
        int err;
@@ -303,6 +377,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
        event = malloc(sizeof(event->bpf) + KSYM_NAME_LEN + machine->id_hdr_size);
        if (!event)
                return -1;
+
+       /* Synthesize all the bpf programs in system. */
        while (true) {
                err = bpf_prog_get_next_id(id, &id);
                if (err) {
@@ -335,6 +411,23 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
                        break;
                }
        }
+
+       /* Synthesize all the bpf images - trampolines/dispatchers. */
+       if (symbol_conf.kallsyms_name != NULL)
+               kallsyms_filename = symbol_conf.kallsyms_name;
+
+       arg = (struct kallsyms_parse) {
+               .event   = event,
+               .process = process,
+               .machine = machine,
+               .tool    = session->tool,
+       };
+
+       if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) {
+               pr_err("%s: failed to synthesize bpf images: %s\n",
+                      __func__, strerror(errno));
+       }
+
        free(event);
        return err;
 }
@@ -416,8 +509,7 @@ static int bpf_event__sb_cb(union perf_event *event, void *data)
        return 0;
 }
 
-int bpf_event__add_sb_event(struct evlist **evlist,
-                           struct perf_env *env)
+int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env)
 {
        struct perf_event_attr attr = {
                .type             = PERF_TYPE_SOFTWARE,
index 81fdc88e6c1a879062e8b36b4af013522bce7688..68f315c3df5bed03c85b7d4e758fe0fd66d7e3bc 100644 (file)
@@ -33,8 +33,7 @@ struct btf_node {
 #ifdef HAVE_LIBBPF_SUPPORT
 int machine__process_bpf(struct machine *machine, union perf_event *event,
                         struct perf_sample *sample);
-int bpf_event__add_sb_event(struct evlist **evlist,
-                                struct perf_env *env);
+int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env);
 void bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
                                    struct perf_env *env,
                                    FILE *fp);
@@ -46,8 +45,8 @@ static inline int machine__process_bpf(struct machine *machine __maybe_unused,
        return 0;
 }
 
-static inline int bpf_event__add_sb_event(struct evlist **evlist __maybe_unused,
-                                         struct perf_env *env __maybe_unused)
+static inline int evlist__add_bpf_sb_event(struct evlist *evlist __maybe_unused,
+                                          struct perf_env *env __maybe_unused)
 {
        return 0;
 }
index 10c187b8b8ead6301175edd860881e6f44f52eb9..83bfb87682350b6896cbd7d7291453203d1eb828 100644 (file)
@@ -1430,7 +1430,7 @@ apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
                return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH;
        }
 
-       if (perf_evsel__is_bpf_output(evsel))
+       if (evsel__is_bpf_output(evsel))
                check_pass = true;
        if (attr->type == PERF_TYPE_RAW)
                check_pass = true;
index 154a05cd03af5c66023f6cfb78e90ccdd226c85b..4d3f02fa223df8c114de5566336302865d6bcd80 100644 (file)
 #include "event.h"
 
 struct branch_flags {
-       u64 mispred:1;
-       u64 predicted:1;
-       u64 in_tx:1;
-       u64 abort:1;
-       u64 cycles:16;
-       u64 type:4;
-       u64 reserved:40;
+       union {
+               u64 value;
+               struct {
+                       u64 mispred:1;
+                       u64 predicted:1;
+                       u64 in_tx:1;
+                       u64 abort:1;
+                       u64 cycles:16;
+                       u64 type:4;
+                       u64 reserved:40;
+               };
+       };
 };
 
 struct branch_info {
index 706bb7bbe1e189382d5a87baef82f93bd447e2fc..8f668ee29f255e2ce230574d0f5ed876d900069d 100644 (file)
@@ -143,6 +143,9 @@ struct callchain_cursor_node {
        u64                             ip;
        struct map_symbol               ms;
        const char                      *srcline;
+       /* Indicate valid cursor node for LBR stitch */
+       bool                            valid;
+
        bool                            branch;
        struct branch_flags             branch_flags;
        u64                             branch_from;
@@ -151,6 +154,11 @@ struct callchain_cursor_node {
        struct callchain_cursor_node    *next;
 };
 
+struct stitch_list {
+       struct list_head                node;
+       struct callchain_cursor_node    cursor;
+};
+
 struct callchain_cursor {
        u64                             nr;
        struct callchain_cursor_node    *first;
index 051dc590ceeebca6a77d9ece65cd159ceb59a31b..ae52878c0b2e69d976159f0e83a65978c2c7f7df 100644 (file)
@@ -29,4 +29,8 @@ static inline bool perf_cap__capable(int cap __maybe_unused)
 #define CAP_SYSLOG     34
 #endif
 
+#ifndef CAP_PERFMON
+#define CAP_PERFMON    38
+#endif
+
 #endif /* __PERF_CAP_H */
index b73fb78230486005f6017238b0682f074086065e..050dea9f1e884d0203da34826c9cb0b8380e08a5 100644 (file)
@@ -107,7 +107,8 @@ found:
 
 static void cgroup__delete(struct cgroup *cgroup)
 {
-       close(cgroup->fd);
+       if (cgroup->fd >= 0)
+               close(cgroup->fd);
        zfree(&cgroup->name);
        free(cgroup);
 }
index a12872f2856ad627903bc387551cae01469f8c1a..6b3988a7aba88a2471d0f0abfbc2f2a86f04443d 100644 (file)
@@ -28,7 +28,7 @@ int __weak sched_getcpu(void)
 
 static int perf_flag_probe(void)
 {
-       /* use 'safest' configuration as used in perf_evsel__fallback() */
+       /* use 'safest' configuration as used in evsel__fallback() */
        struct perf_event_attr attr = {
                .type = PERF_TYPE_SOFTWARE,
                .config = PERF_COUNT_SW_CPU_CLOCK,
index cd92a99eb89debf744857137493489056345a1b0..cd007cc9c283a638b06008a656951a9a6b84e76d 100644 (file)
@@ -564,6 +564,8 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer(
                resp = cs_etm_decoder__set_tid(etmq, packet_queue,
                                               elem, trace_chan_id);
                break;
+       /* Unused packet types */
+       case OCSD_GEN_TRC_ELEM_I_RANGE_NOPATH:
        case OCSD_GEN_TRC_ELEM_ADDR_NACC:
        case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
        case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
index 62d2f9b9ce1b5e14d5987978c4dff61fc9152047..c283223fb31f22479ecfa9e46cdc888c902025c4 100644 (file)
@@ -94,6 +94,9 @@ struct cs_etm_queue {
        struct cs_etm_traceid_queue **traceid_queues;
 };
 
+/* RB tree for quick conversion between traceID and metadata pointers */
+static struct intlist *traceid_list;
+
 static int cs_etm__update_queues(struct cs_etm_auxtrace *etm);
 static int cs_etm__process_queues(struct cs_etm_auxtrace *etm);
 static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
@@ -631,6 +634,16 @@ static void cs_etm__free(struct perf_session *session)
        zfree(&aux);
 }
 
+static bool cs_etm__evsel_is_auxtrace(struct perf_session *session,
+                                     struct evsel *evsel)
+{
+       struct cs_etm_auxtrace *aux = container_of(session->auxtrace,
+                                                  struct cs_etm_auxtrace,
+                                                  auxtrace);
+
+       return evsel->core.attr.type == aux->pmu_type;
+}
+
 static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address)
 {
        struct machine *machine;
@@ -2618,6 +2631,7 @@ int cs_etm__process_auxtrace_info(union perf_event *event,
        etm->auxtrace.flush_events = cs_etm__flush_events;
        etm->auxtrace.free_events = cs_etm__free_events;
        etm->auxtrace.free = cs_etm__free;
+       etm->auxtrace.evsel_is_auxtrace = cs_etm__evsel_is_auxtrace;
        session->auxtrace = &etm->auxtrace;
 
        etm->unknown_thread = thread__new(999999999, 999999999);
index 650ecc2a63492a63b457e8d23e53c49d3f3cc20c..4ad925d6d79966aa913e30cbccf80ccbef70abaa 100644 (file)
@@ -114,9 +114,6 @@ enum cs_etm_isa {
        CS_ETM_ISA_T32,
 };
 
-/* RB tree for quick conversion between traceID and metadata pointers */
-struct intlist *traceid_list;
-
 struct cs_etm_queue;
 
 struct cs_etm_packet {
index dbc772bfb04ecbd6183ef4ccb1519c311ac900bf..5f36fc6a557800325c1b144391156ce9b87f3047 100644 (file)
@@ -835,7 +835,7 @@ static int process_sample_event(struct perf_tool *tool,
                        return -1;
        }
 
-       if (perf_evsel__is_bpf_output(evsel)) {
+       if (evsel__is_bpf_output(evsel)) {
                ret = add_bpf_output_values(event_class, event, sample);
                if (ret)
                        return -1;
@@ -1155,7 +1155,7 @@ static int add_event(struct ctf_writer *cw, struct evsel *evsel)
 {
        struct bt_ctf_event_class *event_class;
        struct evsel_priv *priv;
-       const char *name = perf_evsel__name(evsel);
+       const char *name = evsel__name(evsel);
        int ret;
 
        pr("Adding event '%s' (type %d)\n", name, evsel->core.attr.type);
@@ -1174,7 +1174,7 @@ static int add_event(struct ctf_writer *cw, struct evsel *evsel)
                        goto err;
        }
 
-       if (perf_evsel__is_bpf_output(evsel)) {
+       if (evsel__is_bpf_output(evsel)) {
                ret = add_bpf_output_types(cw, event_class);
                if (ret)
                        goto err;
index 91f21239608bdff57e1f65644e0e326bbe14b369..f338990e0fe65f4bd1ec4acf2896933747dedb09 100644 (file)
@@ -191,6 +191,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
        case DSO_BINARY_TYPE__GUEST_KALLSYMS:
        case DSO_BINARY_TYPE__JAVA_JIT:
        case DSO_BINARY_TYPE__BPF_PROG_INFO:
+       case DSO_BINARY_TYPE__BPF_IMAGE:
        case DSO_BINARY_TYPE__NOT_FOUND:
                ret = -1;
                break;
index 2db64b79617aea0bc45078fca25cac644728e20a..9553a1fd9e8a834012d8acad87ef626c0df8ffa6 100644 (file)
@@ -40,6 +40,7 @@ enum dso_binary_type {
        DSO_BINARY_TYPE__GUEST_KCORE,
        DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
        DSO_BINARY_TYPE__BPF_PROG_INFO,
+       DSO_BINARY_TYPE__BPF_IMAGE,
        DSO_BINARY_TYPE__NOT_FOUND,
 };
 
index 7632075a87922b264f0b54f14d961731300c65a7..1ab2682d5d2b7906d80a159cbec7d4e0b4517c62 100644 (file)
@@ -48,6 +48,7 @@ struct perf_env {
        char                    *cpuid;
        unsigned long long      total_mem;
        unsigned int            msr_pmu_type;
+       unsigned int            max_branches;
 
        int                     nr_cmdline;
        int                     nr_sibling_cores;
@@ -57,12 +58,14 @@ struct perf_env {
        int                     nr_memory_nodes;
        int                     nr_pmu_mappings;
        int                     nr_groups;
+       int                     nr_cpu_pmu_caps;
        char                    *cmdline;
        const char              **cmdline_argv;
        char                    *sibling_cores;
        char                    *sibling_dies;
        char                    *sibling_threads;
        char                    *pmu_mappings;
+       char                    *cpu_pmu_caps;
        struct cpu_topology_map *cpu;
        struct cpu_cache_level  *caches;
        int                      caches_cnt;
index dc0e11214ae1a1678f1796a613d52193c042bce3..f581550a3015d88b8ccde34fd428c3da6287f455 100644 (file)
@@ -626,7 +626,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
                        ret = strlist__has_entry(symbol_conf.sym_list,
                                                al->sym->name);
                }
-               if (!(ret && al->sym)) {
+               if (!ret && al->sym) {
                        snprintf(al_addr_str, sz, "0x%"PRIx64,
                                al->map->unmap_ip(al->map, al->sym->start));
                        ret = strlist__has_entry(symbol_conf.sym_list,
index 1548237b6558d9105dcc07ae8094fb7f06dcc2b4..0a0b760d6948423dbafe407e68a067a6c3a64287 100644 (file)
@@ -23,6 +23,7 @@
 #include "asm/bug.h"
 #include "bpf-event.h"
 #include "util/string2.h"
+#include "util/perf_api_probe.h"
 #include <signal.h>
 #include <unistd.h>
 #include <sched.h>
@@ -118,7 +119,7 @@ static void perf_evlist__update_id_pos(struct evlist *evlist)
        struct evsel *evsel;
 
        evlist__for_each_entry(evlist, evsel)
-               perf_evsel__calc_id_pos(evsel);
+               evsel__calc_id_pos(evsel);
 
        perf_evlist__set_id_pos(evlist);
 }
@@ -390,14 +391,14 @@ void evlist__disable(struct evlist *evlist)
                evlist__for_each_entry(evlist, pos) {
                        if (evsel__cpu_iter_skip(pos, cpu))
                                continue;
-                       if (pos->disabled || !perf_evsel__is_group_leader(pos) || !pos->core.fd)
+                       if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd)
                                continue;
                        evsel__disable_cpu(pos, pos->cpu_iter - 1);
                }
        }
        affinity__cleanup(&affinity);
        evlist__for_each_entry(evlist, pos) {
-               if (!perf_evsel__is_group_leader(pos) || !pos->core.fd)
+               if (!evsel__is_group_leader(pos) || !pos->core.fd)
                        continue;
                pos->disabled = true;
        }
@@ -420,14 +421,14 @@ void evlist__enable(struct evlist *evlist)
                evlist__for_each_entry(evlist, pos) {
                        if (evsel__cpu_iter_skip(pos, cpu))
                                continue;
-                       if (!perf_evsel__is_group_leader(pos) || !pos->core.fd)
+                       if (!evsel__is_group_leader(pos) || !pos->core.fd)
                                continue;
                        evsel__enable_cpu(pos, pos->cpu_iter - 1);
                }
        }
        affinity__cleanup(&affinity);
        evlist__for_each_entry(evlist, pos) {
-               if (!perf_evsel__is_group_leader(pos) || !pos->core.fd)
+               if (!evsel__is_group_leader(pos) || !pos->core.fd)
                        continue;
                pos->disabled = false;
        }
@@ -947,7 +948,7 @@ void __perf_evlist__set_sample_bit(struct evlist *evlist,
        struct evsel *evsel;
 
        evlist__for_each_entry(evlist, evsel)
-               __perf_evsel__set_sample_bit(evsel, bit);
+               __evsel__set_sample_bit(evsel, bit);
 }
 
 void __perf_evlist__reset_sample_bit(struct evlist *evlist,
@@ -956,7 +957,7 @@ void __perf_evlist__reset_sample_bit(struct evlist *evlist,
        struct evsel *evsel;
 
        evlist__for_each_entry(evlist, evsel)
-               __perf_evsel__reset_sample_bit(evsel, bit);
+               __evsel__reset_sample_bit(evsel, bit);
 }
 
 int perf_evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel)
@@ -994,7 +995,7 @@ int perf_evlist__set_tp_filter(struct evlist *evlist, const char *filter)
                if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
                        continue;
 
-               err = perf_evsel__set_filter(evsel, filter);
+               err = evsel__set_filter(evsel, filter);
                if (err)
                        break;
        }
@@ -1014,7 +1015,7 @@ int perf_evlist__append_tp_filter(struct evlist *evlist, const char *filter)
                if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
                        continue;
 
-               err = perf_evsel__append_tp_filter(evsel, filter);
+               err = evsel__append_tp_filter(evsel, filter);
                if (err)
                        break;
        }
@@ -1131,8 +1132,10 @@ bool perf_evlist__valid_read_format(struct evlist *evlist)
        u64 sample_type = first->core.attr.sample_type;
 
        evlist__for_each_entry(evlist, pos) {
-               if (read_format != pos->core.attr.read_format)
-                       return false;
+               if (read_format != pos->core.attr.read_format) {
+                       pr_debug("Read format differs %#" PRIx64 " vs %#" PRIx64 "\n",
+                                read_format, (u64)pos->core.attr.read_format);
+               }
        }
 
        /* PERF_SAMPLE_READ imples PERF_FORMAT_ID. */
@@ -1436,7 +1439,7 @@ int perf_evlist__parse_sample(struct evlist *evlist, union perf_event *event,
 
        if (!evsel)
                return -EFAULT;
-       return perf_evsel__parse_sample(evsel, event, sample);
+       return evsel__parse_sample(evsel, event, sample);
 }
 
 int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
@@ -1447,7 +1450,7 @@ int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
 
        if (!evsel)
                return -EFAULT;
-       return perf_evsel__parse_sample_timestamp(evsel, event, timestamp);
+       return evsel__parse_sample_timestamp(evsel, event, timestamp);
 }
 
 int perf_evlist__strerror_open(struct evlist *evlist,
@@ -1701,133 +1704,3 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
        }
        return leader;
 }
-
-int perf_evlist__add_sb_event(struct evlist **evlist,
-                             struct perf_event_attr *attr,
-                             perf_evsel__sb_cb_t cb,
-                             void *data)
-{
-       struct evsel *evsel;
-       bool new_evlist = (*evlist) == NULL;
-
-       if (*evlist == NULL)
-               *evlist = evlist__new();
-       if (*evlist == NULL)
-               return -1;
-
-       if (!attr->sample_id_all) {
-               pr_warning("enabling sample_id_all for all side band events\n");
-               attr->sample_id_all = 1;
-       }
-
-       evsel = perf_evsel__new_idx(attr, (*evlist)->core.nr_entries);
-       if (!evsel)
-               goto out_err;
-
-       evsel->side_band.cb = cb;
-       evsel->side_band.data = data;
-       evlist__add(*evlist, evsel);
-       return 0;
-
-out_err:
-       if (new_evlist) {
-               evlist__delete(*evlist);
-               *evlist = NULL;
-       }
-       return -1;
-}
-
-static void *perf_evlist__poll_thread(void *arg)
-{
-       struct evlist *evlist = arg;
-       bool draining = false;
-       int i, done = 0;
-       /*
-        * In order to read symbols from other namespaces perf to needs to call
-        * setns(2).  This isn't permitted if the struct_fs has multiple users.
-        * unshare(2) the fs so that we may continue to setns into namespaces
-        * that we're observing when, for instance, reading the build-ids at
-        * the end of a 'perf record' session.
-        */
-       unshare(CLONE_FS);
-
-       while (!done) {
-               bool got_data = false;
-
-               if (evlist->thread.done)
-                       draining = true;
-
-               if (!draining)
-                       evlist__poll(evlist, 1000);
-
-               for (i = 0; i < evlist->core.nr_mmaps; i++) {
-                       struct mmap *map = &evlist->mmap[i];
-                       union perf_event *event;
-
-                       if (perf_mmap__read_init(&map->core))
-                               continue;
-                       while ((event = perf_mmap__read_event(&map->core)) != NULL) {
-                               struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
-
-                               if (evsel && evsel->side_band.cb)
-                                       evsel->side_band.cb(event, evsel->side_band.data);
-                               else
-                                       pr_warning("cannot locate proper evsel for the side band event\n");
-
-                               perf_mmap__consume(&map->core);
-                               got_data = true;
-                       }
-                       perf_mmap__read_done(&map->core);
-               }
-
-               if (draining && !got_data)
-                       break;
-       }
-       return NULL;
-}
-
-int perf_evlist__start_sb_thread(struct evlist *evlist,
-                                struct target *target)
-{
-       struct evsel *counter;
-
-       if (!evlist)
-               return 0;
-
-       if (perf_evlist__create_maps(evlist, target))
-               goto out_delete_evlist;
-
-       evlist__for_each_entry(evlist, counter) {
-               if (evsel__open(counter, evlist->core.cpus,
-                                    evlist->core.threads) < 0)
-                       goto out_delete_evlist;
-       }
-
-       if (evlist__mmap(evlist, UINT_MAX))
-               goto out_delete_evlist;
-
-       evlist__for_each_entry(evlist, counter) {
-               if (evsel__enable(counter))
-                       goto out_delete_evlist;
-       }
-
-       evlist->thread.done = 0;
-       if (pthread_create(&evlist->thread.th, NULL, perf_evlist__poll_thread, evlist))
-               goto out_delete_evlist;
-
-       return 0;
-
-out_delete_evlist:
-       evlist__delete(evlist);
-       evlist = NULL;
-       return -1;
-}
-
-void perf_evlist__stop_sb_thread(struct evlist *evlist)
-{
-       if (!evlist)
-               return;
-       evlist->thread.done = 1;
-       pthread_join(evlist->thread.th, NULL);
-       evlist__delete(evlist);
-}
index f5bd5c386df1138423313860c34ab331d34024a4..b6f325dfb4d24d8481421be75c98b9567a9b610a 100644 (file)
@@ -107,10 +107,11 @@ int __perf_evlist__add_default_attrs(struct evlist *evlist,
 
 int perf_evlist__add_dummy(struct evlist *evlist);
 
-int perf_evlist__add_sb_event(struct evlist **evlist,
+int perf_evlist__add_sb_event(struct evlist *evlist,
                              struct perf_event_attr *attr,
-                             perf_evsel__sb_cb_t cb,
+                             evsel__sb_cb_t cb,
                              void *data);
+void evlist__set_cb(struct evlist *evlist, evsel__sb_cb_t cb, void *data);
 int perf_evlist__start_sb_thread(struct evlist *evlist,
                                 struct target *target);
 void perf_evlist__stop_sb_thread(struct evlist *evlist);
@@ -173,10 +174,6 @@ void evlist__close(struct evlist *evlist);
 struct callchain_param;
 
 void perf_evlist__set_id_pos(struct evlist *evlist);
-bool perf_can_sample_identifier(void);
-bool perf_can_record_switch_events(void);
-bool perf_can_record_cpu_wide(void);
-bool perf_can_aux_sample(void);
 void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
                         struct callchain_param *callchain);
 int record_opts__config(struct record_opts *opts);
index eb880efbce16dcadec399cc4530dd0ba47ee630a..f3e60c45d59ad0ab38444f836f8b721ff549bb80 100644 (file)
@@ -102,7 +102,7 @@ set_methods:
 
 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
 
-int __perf_evsel__sample_size(u64 sample_type)
+int __evsel__sample_size(u64 sample_type)
 {
        u64 mask = sample_type & PERF_SAMPLE_MASK;
        int size = 0;
@@ -178,53 +178,53 @@ static int __perf_evsel__calc_is_pos(u64 sample_type)
        return idx;
 }
 
-void perf_evsel__calc_id_pos(struct evsel *evsel)
+void evsel__calc_id_pos(struct evsel *evsel)
 {
        evsel->id_pos = __perf_evsel__calc_id_pos(evsel->core.attr.sample_type);
        evsel->is_pos = __perf_evsel__calc_is_pos(evsel->core.attr.sample_type);
 }
 
-void __perf_evsel__set_sample_bit(struct evsel *evsel,
+void __evsel__set_sample_bit(struct evsel *evsel,
                                  enum perf_event_sample_format bit)
 {
        if (!(evsel->core.attr.sample_type & bit)) {
                evsel->core.attr.sample_type |= bit;
                evsel->sample_size += sizeof(u64);
-               perf_evsel__calc_id_pos(evsel);
+               evsel__calc_id_pos(evsel);
        }
 }
 
-void __perf_evsel__reset_sample_bit(struct evsel *evsel,
+void __evsel__reset_sample_bit(struct evsel *evsel,
                                    enum perf_event_sample_format bit)
 {
        if (evsel->core.attr.sample_type & bit) {
                evsel->core.attr.sample_type &= ~bit;
                evsel->sample_size -= sizeof(u64);
-               perf_evsel__calc_id_pos(evsel);
+               evsel__calc_id_pos(evsel);
        }
 }
 
-void perf_evsel__set_sample_id(struct evsel *evsel,
+void evsel__set_sample_id(struct evsel *evsel,
                               bool can_sample_identifier)
 {
        if (can_sample_identifier) {
-               perf_evsel__reset_sample_bit(evsel, ID);
-               perf_evsel__set_sample_bit(evsel, IDENTIFIER);
+               evsel__reset_sample_bit(evsel, ID);
+               evsel__set_sample_bit(evsel, IDENTIFIER);
        } else {
-               perf_evsel__set_sample_bit(evsel, ID);
+               evsel__set_sample_bit(evsel, ID);
        }
        evsel->core.attr.read_format |= PERF_FORMAT_ID;
 }
 
 /**
- * perf_evsel__is_function_event - Return whether given evsel is a function
+ * evsel__is_function_event - Return whether given evsel is a function
  * trace event
  *
  * @evsel - evsel selector to be tested
  *
  * Return %true if event is function trace event
  */
-bool perf_evsel__is_function_event(struct evsel *evsel)
+bool evsel__is_function_event(struct evsel *evsel)
 {
 #define FUNCTION_EVENT "ftrace:function"
 
@@ -249,8 +249,8 @@ void evsel__init(struct evsel *evsel,
        evsel->bpf_fd      = -1;
        INIT_LIST_HEAD(&evsel->config_terms);
        perf_evsel__object.init(evsel);
-       evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
-       perf_evsel__calc_id_pos(evsel);
+       evsel->sample_size = __evsel__sample_size(attr->sample_type);
+       evsel__calc_id_pos(evsel);
        evsel->cmdline_group_boundary = false;
        evsel->metric_expr   = NULL;
        evsel->metric_name   = NULL;
@@ -267,13 +267,13 @@ struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
                return NULL;
        evsel__init(evsel, attr, idx);
 
-       if (perf_evsel__is_bpf_output(evsel)) {
+       if (evsel__is_bpf_output(evsel)) {
                evsel->core.attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
                                            PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),
                evsel->core.attr.sample_period = 1;
        }
 
-       if (perf_evsel__is_clock(evsel)) {
+       if (evsel__is_clock(evsel)) {
                /*
                 * The evsel->unit points to static alias->unit
                 * so it's ok to use static string in here.
@@ -385,7 +385,7 @@ const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
        "ref-cycles",
 };
 
-static const char *__perf_evsel__hw_name(u64 config)
+static const char *__evsel__hw_name(u64 config)
 {
        if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
                return perf_evsel__hw_names[config];
@@ -429,9 +429,9 @@ static int perf_evsel__add_modifiers(struct evsel *evsel, char *bf, size_t size)
        return r;
 }
 
-static int perf_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
+static int evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
 {
-       int r = scnprintf(bf, size, "%s", __perf_evsel__hw_name(evsel->core.attr.config));
+       int r = scnprintf(bf, size, "%s", __evsel__hw_name(evsel->core.attr.config));
        return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
 }
 
@@ -448,20 +448,20 @@ const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
        "dummy",
 };
 
-static const char *__perf_evsel__sw_name(u64 config)
+static const char *__evsel__sw_name(u64 config)
 {
        if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config])
                return perf_evsel__sw_names[config];
        return "unknown-software";
 }
 
-static int perf_evsel__sw_name(struct evsel *evsel, char *bf, size_t size)
+static int evsel__sw_name(struct evsel *evsel, char *bf, size_t size)
 {
-       int r = scnprintf(bf, size, "%s", __perf_evsel__sw_name(evsel->core.attr.config));
+       int r = scnprintf(bf, size, "%s", __evsel__sw_name(evsel->core.attr.config));
        return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
 }
 
-static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
+static int __evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
 {
        int r;
 
@@ -479,10 +479,10 @@ static int __perf_evsel__bp_name(char *bf, size_t size, u64 addr, u64 type)
        return r;
 }
 
-static int perf_evsel__bp_name(struct evsel *evsel, char *bf, size_t size)
+static int evsel__bp_name(struct evsel *evsel, char *bf, size_t size)
 {
        struct perf_event_attr *attr = &evsel->core.attr;
-       int r = __perf_evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type);
+       int r = __evsel__bp_name(bf, size, attr->bp_addr, attr->bp_type);
        return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
 }
 
@@ -531,7 +531,7 @@ static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
  [C(NODE)]     = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
 };
 
-bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
+bool evsel__is_cache_op_valid(u8 type, u8 op)
 {
        if (perf_evsel__hw_cache_stat[type] & COP(op))
                return true;    /* valid */
@@ -539,8 +539,7 @@ bool perf_evsel__is_cache_op_valid(u8 type, u8 op)
                return false;   /* invalid */
 }
 
-int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
-                                           char *bf, size_t size)
+int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size)
 {
        if (result) {
                return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
@@ -552,7 +551,7 @@ int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
                         perf_evsel__hw_cache_op[op][1]);
 }
 
-static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
+static int __evsel__hw_cache_name(u64 config, char *bf, size_t size)
 {
        u8 op, result, type = (config >>  0) & 0xff;
        const char *err = "unknown-ext-hardware-cache-type";
@@ -571,33 +570,33 @@ static int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size)
                goto out_err;
 
        err = "invalid-cache";
-       if (!perf_evsel__is_cache_op_valid(type, op))
+       if (!evsel__is_cache_op_valid(type, op))
                goto out_err;
 
-       return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
+       return __evsel__hw_cache_type_op_res_name(type, op, result, bf, size);
 out_err:
        return scnprintf(bf, size, "%s", err);
 }
 
-static int perf_evsel__hw_cache_name(struct evsel *evsel, char *bf, size_t size)
+static int evsel__hw_cache_name(struct evsel *evsel, char *bf, size_t size)
 {
-       int ret = __perf_evsel__hw_cache_name(evsel->core.attr.config, bf, size);
+       int ret = __evsel__hw_cache_name(evsel->core.attr.config, bf, size);
        return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
 }
 
-static int perf_evsel__raw_name(struct evsel *evsel, char *bf, size_t size)
+static int evsel__raw_name(struct evsel *evsel, char *bf, size_t size)
 {
        int ret = scnprintf(bf, size, "raw 0x%" PRIx64, evsel->core.attr.config);
        return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret);
 }
 
-static int perf_evsel__tool_name(char *bf, size_t size)
+static int evsel__tool_name(char *bf, size_t size)
 {
        int ret = scnprintf(bf, size, "duration_time");
        return ret;
 }
 
-const char *perf_evsel__name(struct evsel *evsel)
+const char *evsel__name(struct evsel *evsel)
 {
        char bf[128];
 
@@ -609,22 +608,22 @@ const char *perf_evsel__name(struct evsel *evsel)
 
        switch (evsel->core.attr.type) {
        case PERF_TYPE_RAW:
-               perf_evsel__raw_name(evsel, bf, sizeof(bf));
+               evsel__raw_name(evsel, bf, sizeof(bf));
                break;
 
        case PERF_TYPE_HARDWARE:
-               perf_evsel__hw_name(evsel, bf, sizeof(bf));
+               evsel__hw_name(evsel, bf, sizeof(bf));
                break;
 
        case PERF_TYPE_HW_CACHE:
-               perf_evsel__hw_cache_name(evsel, bf, sizeof(bf));
+               evsel__hw_cache_name(evsel, bf, sizeof(bf));
                break;
 
        case PERF_TYPE_SOFTWARE:
                if (evsel->tool_event)
-                       perf_evsel__tool_name(bf, sizeof(bf));
+                       evsel__tool_name(bf, sizeof(bf));
                else
-                       perf_evsel__sw_name(evsel, bf, sizeof(bf));
+                       evsel__sw_name(evsel, bf, sizeof(bf));
                break;
 
        case PERF_TYPE_TRACEPOINT:
@@ -632,7 +631,7 @@ const char *perf_evsel__name(struct evsel *evsel)
                break;
 
        case PERF_TYPE_BREAKPOINT:
-               perf_evsel__bp_name(evsel, bf, sizeof(bf));
+               evsel__bp_name(evsel, bf, sizeof(bf));
                break;
 
        default:
@@ -649,7 +648,7 @@ out_unknown:
        return "unknown";
 }
 
-const char *perf_evsel__group_name(struct evsel *evsel)
+const char *evsel__group_name(struct evsel *evsel)
 {
        return evsel->group_name ?: "anon group";
 }
@@ -664,21 +663,19 @@ const char *perf_evsel__group_name(struct evsel *evsel)
  *  For record -e 'cycles,instructions' and report --group
  *    'cycles:u, instructions:u'
  */
-int perf_evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
+int evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
 {
        int ret = 0;
        struct evsel *pos;
-       const char *group_name = perf_evsel__group_name(evsel);
+       const char *group_name = evsel__group_name(evsel);
 
        if (!evsel->forced_leader)
                ret = scnprintf(buf, size, "%s { ", group_name);
 
-       ret += scnprintf(buf + ret, size - ret, "%s",
-                        perf_evsel__name(evsel));
+       ret += scnprintf(buf + ret, size - ret, "%s", evsel__name(evsel));
 
        for_each_group_member(pos, evsel)
-               ret += scnprintf(buf + ret, size - ret, ", %s",
-                                perf_evsel__name(pos));
+               ret += scnprintf(buf + ret, size - ret, ", %s", evsel__name(pos));
 
        if (!evsel->forced_leader)
                ret += scnprintf(buf + ret, size - ret, " }");
@@ -686,14 +683,13 @@ int perf_evsel__group_desc(struct evsel *evsel, char *buf, size_t size)
        return ret;
 }
 
-static void __perf_evsel__config_callchain(struct evsel *evsel,
-                                          struct record_opts *opts,
-                                          struct callchain_param *param)
+static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
+                                     struct callchain_param *param)
 {
-       bool function = perf_evsel__is_function_event(evsel);
+       bool function = evsel__is_function_event(evsel);
        struct perf_event_attr *attr = &evsel->core.attr;
 
-       perf_evsel__set_sample_bit(evsel, CALLCHAIN);
+       evsel__set_sample_bit(evsel, CALLCHAIN);
 
        attr->sample_max_stack = param->max_stack;
 
@@ -708,7 +704,7 @@ static void __perf_evsel__config_callchain(struct evsel *evsel,
                                           "to get user callchain information. "
                                           "Falling back to framepointers.\n");
                        } else {
-                               perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+                               evsel__set_sample_bit(evsel, BRANCH_STACK);
                                attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
                                                        PERF_SAMPLE_BRANCH_CALL_STACK |
                                                        PERF_SAMPLE_BRANCH_NO_CYCLES |
@@ -722,8 +718,8 @@ static void __perf_evsel__config_callchain(struct evsel *evsel,
 
        if (param->record_mode == CALLCHAIN_DWARF) {
                if (!function) {
-                       perf_evsel__set_sample_bit(evsel, REGS_USER);
-                       perf_evsel__set_sample_bit(evsel, STACK_USER);
+                       evsel__set_sample_bit(evsel, REGS_USER);
+                       evsel__set_sample_bit(evsel, STACK_USER);
                        if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) {
                                attr->sample_regs_user |= DWARF_MINIMAL_REGS;
                                pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
@@ -746,12 +742,11 @@ static void __perf_evsel__config_callchain(struct evsel *evsel,
        }
 }
 
-void perf_evsel__config_callchain(struct evsel *evsel,
-                                 struct record_opts *opts,
-                                 struct callchain_param *param)
+void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
+                            struct callchain_param *param)
 {
        if (param->enabled)
-               return __perf_evsel__config_callchain(evsel, opts, param);
+               return __evsel__config_callchain(evsel, opts, param);
 }
 
 static void
@@ -760,16 +755,16 @@ perf_evsel__reset_callgraph(struct evsel *evsel,
 {
        struct perf_event_attr *attr = &evsel->core.attr;
 
-       perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
+       evsel__reset_sample_bit(evsel, CALLCHAIN);
        if (param->record_mode == CALLCHAIN_LBR) {
-               perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
+               evsel__reset_sample_bit(evsel, BRANCH_STACK);
                attr->branch_sample_type &= ~(PERF_SAMPLE_BRANCH_USER |
                                              PERF_SAMPLE_BRANCH_CALL_STACK |
                                              PERF_SAMPLE_BRANCH_HW_INDEX);
        }
        if (param->record_mode == CALLCHAIN_DWARF) {
-               perf_evsel__reset_sample_bit(evsel, REGS_USER);
-               perf_evsel__reset_sample_bit(evsel, STACK_USER);
+               evsel__reset_sample_bit(evsel, REGS_USER);
+               evsel__reset_sample_bit(evsel, STACK_USER);
        }
 }
 
@@ -793,32 +788,32 @@ static void apply_config_terms(struct evsel *evsel,
                        if (!(term->weak && opts->user_interval != ULLONG_MAX)) {
                                attr->sample_period = term->val.period;
                                attr->freq = 0;
-                               perf_evsel__reset_sample_bit(evsel, PERIOD);
+                               evsel__reset_sample_bit(evsel, PERIOD);
                        }
                        break;
                case PERF_EVSEL__CONFIG_TERM_FREQ:
                        if (!(term->weak && opts->user_freq != UINT_MAX)) {
                                attr->sample_freq = term->val.freq;
                                attr->freq = 1;
-                               perf_evsel__set_sample_bit(evsel, PERIOD);
+                               evsel__set_sample_bit(evsel, PERIOD);
                        }
                        break;
                case PERF_EVSEL__CONFIG_TERM_TIME:
                        if (term->val.time)
-                               perf_evsel__set_sample_bit(evsel, TIME);
+                               evsel__set_sample_bit(evsel, TIME);
                        else
-                               perf_evsel__reset_sample_bit(evsel, TIME);
+                               evsel__reset_sample_bit(evsel, TIME);
                        break;
                case PERF_EVSEL__CONFIG_TERM_CALLGRAPH:
                        callgraph_buf = term->val.str;
                        break;
                case PERF_EVSEL__CONFIG_TERM_BRANCH:
                        if (term->val.str && strcmp(term->val.str, "no")) {
-                               perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+                               evsel__set_sample_bit(evsel, BRANCH_STACK);
                                parse_branch_str(term->val.str,
                                                 &attr->branch_sample_type);
                        } else
-                               perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
+                               evsel__reset_sample_bit(evsel, BRANCH_STACK);
                        break;
                case PERF_EVSEL__CONFIG_TERM_STACK_USER:
                        dump_size = term->val.stack_user;
@@ -832,7 +827,7 @@ static void apply_config_terms(struct evsel *evsel,
                case PERF_EVSEL__CONFIG_TERM_INHERIT:
                        /*
                         * attr->inherit should has already been set by
-                        * perf_evsel__config. If user explicitly set
+                        * evsel__config. If user explicitly set
                         * inherit using config terms, override global
                         * opt->no_inherit setting.
                         */
@@ -897,11 +892,11 @@ static void apply_config_terms(struct evsel *evsel,
                /* set perf-event callgraph */
                if (param.enabled) {
                        if (sample_address) {
-                               perf_evsel__set_sample_bit(evsel, ADDR);
-                               perf_evsel__set_sample_bit(evsel, DATA_SRC);
+                               evsel__set_sample_bit(evsel, ADDR);
+                               evsel__set_sample_bit(evsel, DATA_SRC);
                                evsel->core.attr.mmap_data = track;
                        }
-                       perf_evsel__config_callchain(evsel, opts, &param);
+                       evsel__config_callchain(evsel, opts, &param);
                }
        }
 }
@@ -953,8 +948,8 @@ struct perf_evsel_config_term *__perf_evsel__get_config_term(struct evsel *evsel
  *     enable/disable events specifically, as there's no
  *     initial traced exec call.
  */
-void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
-                       struct callchain_param *callchain)
+void evsel__config(struct evsel *evsel, struct record_opts *opts,
+                  struct callchain_param *callchain)
 {
        struct evsel *leader = evsel->leader;
        struct perf_event_attr *attr = &evsel->core.attr;
@@ -965,17 +960,17 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
        attr->inherit       = !opts->no_inherit;
        attr->write_backward = opts->overwrite ? 1 : 0;
 
-       perf_evsel__set_sample_bit(evsel, IP);
-       perf_evsel__set_sample_bit(evsel, TID);
+       evsel__set_sample_bit(evsel, IP);
+       evsel__set_sample_bit(evsel, TID);
 
        if (evsel->sample_read) {
-               perf_evsel__set_sample_bit(evsel, READ);
+               evsel__set_sample_bit(evsel, READ);
 
                /*
                 * We need ID even in case of single event, because
                 * PERF_SAMPLE_READ process ID specific data.
                 */
-               perf_evsel__set_sample_id(evsel, false);
+               evsel__set_sample_id(evsel, false);
 
                /*
                 * Apply group format only if we belong to group
@@ -994,7 +989,7 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
        if (!attr->sample_period || (opts->user_freq != UINT_MAX ||
                                     opts->user_interval != ULLONG_MAX)) {
                if (opts->freq) {
-                       perf_evsel__set_sample_bit(evsel, PERIOD);
+                       evsel__set_sample_bit(evsel, PERIOD);
                        attr->freq              = 1;
                        attr->sample_freq       = opts->freq;
                } else {
@@ -1002,25 +997,6 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
                }
        }
 
-       /*
-        * Disable sampling for all group members other
-        * than leader in case leader 'leads' the sampling.
-        */
-       if ((leader != evsel) && leader->sample_read) {
-               attr->freq           = 0;
-               attr->sample_freq    = 0;
-               attr->sample_period  = 0;
-               attr->write_backward = 0;
-
-               /*
-                * We don't get sample for slave events, we make them
-                * when delivering group leader sample. Set the slave
-                * event to follow the master sample_type to ease up
-                * report.
-                */
-               attr->sample_type = leader->core.attr.sample_type;
-       }
-
        if (opts->no_samples)
                attr->sample_freq = 0;
 
@@ -1033,7 +1009,7 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
        }
 
        if (opts->sample_address) {
-               perf_evsel__set_sample_bit(evsel, ADDR);
+               evsel__set_sample_bit(evsel, ADDR);
                attr->mmap_data = track;
        }
 
@@ -1042,24 +1018,24 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
         * event, due to issues with page faults while tracing page
         * fault handler and its overall trickiness nature.
         */
-       if (perf_evsel__is_function_event(evsel))
+       if (evsel__is_function_event(evsel))
                evsel->core.attr.exclude_callchain_user = 1;
 
        if (callchain && callchain->enabled && !evsel->no_aux_samples)
-               perf_evsel__config_callchain(evsel, opts, callchain);
+               evsel__config_callchain(evsel, opts, callchain);
 
        if (opts->sample_intr_regs) {
                attr->sample_regs_intr = opts->sample_intr_regs;
-               perf_evsel__set_sample_bit(evsel, REGS_INTR);
+               evsel__set_sample_bit(evsel, REGS_INTR);
        }
 
        if (opts->sample_user_regs) {
                attr->sample_regs_user |= opts->sample_user_regs;
-               perf_evsel__set_sample_bit(evsel, REGS_USER);
+               evsel__set_sample_bit(evsel, REGS_USER);
        }
 
        if (target__has_cpu(&opts->target) || opts->sample_cpu)
-               perf_evsel__set_sample_bit(evsel, CPU);
+               evsel__set_sample_bit(evsel, CPU);
 
        /*
         * When the user explicitly disabled time don't force it here.
@@ -1068,31 +1044,31 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
            (!perf_missing_features.sample_id_all &&
            (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu ||
             opts->sample_time_set)))
-               perf_evsel__set_sample_bit(evsel, TIME);
+               evsel__set_sample_bit(evsel, TIME);
 
        if (opts->raw_samples && !evsel->no_aux_samples) {
-               perf_evsel__set_sample_bit(evsel, TIME);
-               perf_evsel__set_sample_bit(evsel, RAW);
-               perf_evsel__set_sample_bit(evsel, CPU);
+               evsel__set_sample_bit(evsel, TIME);
+               evsel__set_sample_bit(evsel, RAW);
+               evsel__set_sample_bit(evsel, CPU);
        }
 
        if (opts->sample_address)
-               perf_evsel__set_sample_bit(evsel, DATA_SRC);
+               evsel__set_sample_bit(evsel, DATA_SRC);
 
        if (opts->sample_phys_addr)
-               perf_evsel__set_sample_bit(evsel, PHYS_ADDR);
+               evsel__set_sample_bit(evsel, PHYS_ADDR);
 
        if (opts->no_buffering) {
                attr->watermark = 0;
                attr->wakeup_events = 1;
        }
        if (opts->branch_stack && !evsel->no_aux_samples) {
-               perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+               evsel__set_sample_bit(evsel, BRANCH_STACK);
                attr->branch_sample_type = opts->branch_stack;
        }
 
        if (opts->sample_weight)
-               perf_evsel__set_sample_bit(evsel, WEIGHT);
+               evsel__set_sample_bit(evsel, WEIGHT);
 
        attr->task  = track;
        attr->mmap  = track;
@@ -1106,14 +1082,14 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
 
        if (opts->record_cgroup) {
                attr->cgroup = track && !perf_missing_features.cgroup;
-               perf_evsel__set_sample_bit(evsel, CGROUP);
+               evsel__set_sample_bit(evsel, CGROUP);
        }
 
        if (opts->record_switch_events)
                attr->context_switch = track;
 
        if (opts->sample_transaction)
-               perf_evsel__set_sample_bit(evsel, TRANSACTION);
+               evsel__set_sample_bit(evsel, TRANSACTION);
 
        if (opts->running_time) {
                evsel->core.attr.read_format |=
@@ -1127,15 +1103,15 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
         * Disabling only independent events or group leaders,
         * keeping group members enabled.
         */
-       if (perf_evsel__is_group_leader(evsel))
+       if (evsel__is_group_leader(evsel))
                attr->disabled = 1;
 
        /*
         * Setting enable_on_exec for independent events and
         * group leaders for traced executed by perf.
         */
-       if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) &&
-               !opts->initial_delay)
+       if (target__none(&opts->target) && evsel__is_group_leader(evsel) &&
+           !opts->initial_delay)
                attr->enable_on_exec = 1;
 
        if (evsel->immediate) {
@@ -1176,9 +1152,9 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
        /* The --period option takes the precedence. */
        if (opts->period_set) {
                if (opts->period)
-                       perf_evsel__set_sample_bit(evsel, PERIOD);
+                       evsel__set_sample_bit(evsel, PERIOD);
                else
-                       perf_evsel__reset_sample_bit(evsel, PERIOD);
+                       evsel__reset_sample_bit(evsel, PERIOD);
        }
 
        /*
@@ -1187,10 +1163,10 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,
         * if BRANCH_STACK bit is set.
         */
        if (opts->initial_delay && is_dummy_event(evsel))
-               perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
+               evsel__reset_sample_bit(evsel, BRANCH_STACK);
 }
 
-int perf_evsel__set_filter(struct evsel *evsel, const char *filter)
+int evsel__set_filter(struct evsel *evsel, const char *filter)
 {
        char *new_filter = strdup(filter);
 
@@ -1203,13 +1179,12 @@ int perf_evsel__set_filter(struct evsel *evsel, const char *filter)
        return -1;
 }
 
-static int perf_evsel__append_filter(struct evsel *evsel,
-                                    const char *fmt, const char *filter)
+static int evsel__append_filter(struct evsel *evsel, const char *fmt, const char *filter)
 {
        char *new_filter;
 
        if (evsel->filter == NULL)
-               return perf_evsel__set_filter(evsel, filter);
+               return evsel__set_filter(evsel, filter);
 
        if (asprintf(&new_filter, fmt, evsel->filter, filter) > 0) {
                free(evsel->filter);
@@ -1220,14 +1195,14 @@ static int perf_evsel__append_filter(struct evsel *evsel,
        return -1;
 }
 
-int perf_evsel__append_tp_filter(struct evsel *evsel, const char *filter)
+int evsel__append_tp_filter(struct evsel *evsel, const char *filter)
 {
-       return perf_evsel__append_filter(evsel, "(%s) && (%s)", filter);
+       return evsel__append_filter(evsel, "(%s) && (%s)", filter);
 }
 
-int perf_evsel__append_addr_filter(struct evsel *evsel, const char *filter)
+int evsel__append_addr_filter(struct evsel *evsel, const char *filter)
 {
-       return perf_evsel__append_filter(evsel, "%s,%s", filter);
+       return evsel__append_filter(evsel, "%s,%s", filter);
 }
 
 /* Caller has to clear disabled after going through all CPUs. */
@@ -1278,7 +1253,7 @@ static void perf_evsel__free_config_terms(struct evsel *evsel)
        }
 }
 
-void perf_evsel__exit(struct evsel *evsel)
+void evsel__exit(struct evsel *evsel)
 {
        assert(list_empty(&evsel->core.node));
        assert(evsel->evlist == NULL);
@@ -1298,12 +1273,12 @@ void perf_evsel__exit(struct evsel *evsel)
 
 void evsel__delete(struct evsel *evsel)
 {
-       perf_evsel__exit(evsel);
+       evsel__exit(evsel);
        free(evsel);
 }
 
-void perf_evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
-                               struct perf_counts_values *count)
+void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
+                          struct perf_counts_values *count)
 {
        struct perf_counts_values tmp;
 
@@ -1342,8 +1317,7 @@ void perf_counts_values__scale(struct perf_counts_values *count,
                *pscaled = scaled;
 }
 
-static int
-perf_evsel__read_one(struct evsel *evsel, int cpu, int thread)
+static int evsel__read_one(struct evsel *evsel, int cpu, int thread)
 {
        struct perf_counts_values *count = perf_counts(evsel->counts, cpu, thread);
 
@@ -1403,8 +1377,7 @@ perf_evsel__process_group_data(struct evsel *leader,
        return 0;
 }
 
-static int
-perf_evsel__read_group(struct evsel *leader, int cpu, int thread)
+static int evsel__read_group(struct evsel *leader, int cpu, int thread)
 {
        struct perf_stat_evsel *ps = leader->stats;
        u64 read_format = leader->core.attr.read_format;
@@ -1414,7 +1387,7 @@ perf_evsel__read_group(struct evsel *leader, int cpu, int thread)
        if (!(read_format & PERF_FORMAT_ID))
                return -EINVAL;
 
-       if (!perf_evsel__is_group_leader(leader))
+       if (!evsel__is_group_leader(leader))
                return -EINVAL;
 
        if (!data) {
@@ -1434,18 +1407,17 @@ perf_evsel__read_group(struct evsel *leader, int cpu, int thread)
        return perf_evsel__process_group_data(leader, cpu, thread, data);
 }
 
-int perf_evsel__read_counter(struct evsel *evsel, int cpu, int thread)
+int evsel__read_counter(struct evsel *evsel, int cpu, int thread)
 {
        u64 read_format = evsel->core.attr.read_format;
 
        if (read_format & PERF_FORMAT_GROUP)
-               return perf_evsel__read_group(evsel, cpu, thread);
-       else
-               return perf_evsel__read_one(evsel, cpu, thread);
+               return evsel__read_group(evsel, cpu, thread);
+
+       return evsel__read_one(evsel, cpu, thread);
 }
 
-int __perf_evsel__read_on_cpu(struct evsel *evsel,
-                             int cpu, int thread, bool scale)
+int __evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread, bool scale)
 {
        struct perf_counts_values count;
        size_t nv = scale ? 3 : 1;
@@ -1459,7 +1431,7 @@ int __perf_evsel__read_on_cpu(struct evsel *evsel,
        if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) <= 0)
                return -errno;
 
-       perf_evsel__compute_deltas(evsel, cpu, thread, &count);
+       evsel__compute_deltas(evsel, cpu, thread, &count);
        perf_counts_values__scale(&count, scale, NULL);
        *perf_counts(evsel->counts, cpu, thread) = count;
        return 0;
@@ -1470,7 +1442,7 @@ static int get_group_fd(struct evsel *evsel, int cpu, int thread)
        struct evsel *leader = evsel->leader;
        int fd;
 
-       if (perf_evsel__is_group_leader(evsel))
+       if (evsel__is_group_leader(evsel))
                return -1;
 
        /*
@@ -1749,8 +1721,7 @@ retry_open:
 
                        /*
                         * If we succeeded but had to kill clockid, fail and
-                        * have perf_evsel__open_strerror() print us a nice
-                        * error.
+                        * have evsel__open_strerror() print us a nice error.
                         */
                        if (perf_missing_features.clockid ||
                            perf_missing_features.clockid_wrong) {
@@ -1854,7 +1825,7 @@ try_fallback:
        } else if (!perf_missing_features.group_read &&
                    evsel->core.attr.inherit &&
                   (evsel->core.attr.read_format & PERF_FORMAT_GROUP) &&
-                  perf_evsel__is_group_leader(evsel)) {
+                  evsel__is_group_leader(evsel)) {
                perf_missing_features.group_read = true;
                pr_debug2_peo("switching off group read\n");
                goto fallback_missing_features;
@@ -1888,9 +1859,7 @@ void evsel__close(struct evsel *evsel)
        perf_evsel__free_id(&evsel->core);
 }
 
-int perf_evsel__open_per_cpu(struct evsel *evsel,
-                            struct perf_cpu_map *cpus,
-                            int cpu)
+int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu)
 {
        if (cpu == -1)
                return evsel__open_cpu(evsel, cpus, NULL, 0,
@@ -1899,8 +1868,7 @@ int perf_evsel__open_per_cpu(struct evsel *evsel,
        return evsel__open_cpu(evsel, cpus, NULL, cpu, cpu + 1);
 }
 
-int perf_evsel__open_per_thread(struct evsel *evsel,
-                               struct perf_thread_map *threads)
+int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads)
 {
        return evsel__open(evsel, NULL, threads);
 }
@@ -1995,8 +1963,8 @@ perf_event__check_size(union perf_event *event, unsigned int sample_size)
        return 0;
 }
 
-int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
-                            struct perf_sample *data)
+int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
+                       struct perf_sample *data)
 {
        u64 type = evsel->core.attr.sample_type;
        bool swapped = evsel->needs_swap;
@@ -2136,7 +2104,7 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
                }
        }
 
-       if (evsel__has_callchain(evsel)) {
+       if (type & PERF_SAMPLE_CALLCHAIN) {
                const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);
 
                OVERFLOW_CHECK_u64(array);
@@ -2190,7 +2158,7 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
                        return -EFAULT;
 
                sz = data->branch_stack->nr * sizeof(struct branch_entry);
-               if (perf_evsel__has_branch_hw_idx(evsel))
+               if (evsel__has_branch_hw_idx(evsel))
                        sz += sizeof(u64);
                else
                        data->no_hw_idx = true;
@@ -2298,9 +2266,8 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
        return 0;
 }
 
-int perf_evsel__parse_sample_timestamp(struct evsel *evsel,
-                                      union perf_event *event,
-                                      u64 *timestamp)
+int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event,
+                                 u64 *timestamp)
 {
        u64 type = evsel->core.attr.sample_type;
        const __u64 *array;
@@ -2342,15 +2309,14 @@ int perf_evsel__parse_sample_timestamp(struct evsel *evsel,
        return 0;
 }
 
-struct tep_format_field *perf_evsel__field(struct evsel *evsel, const char *name)
+struct tep_format_field *evsel__field(struct evsel *evsel, const char *name)
 {
        return tep_find_field(evsel->tp_format, name);
 }
 
-void *perf_evsel__rawptr(struct evsel *evsel, struct perf_sample *sample,
-                        const char *name)
+void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char *name)
 {
-       struct tep_format_field *field = perf_evsel__field(evsel, name);
+       struct tep_format_field *field = evsel__field(evsel, name);
        int offset;
 
        if (!field)
@@ -2405,10 +2371,9 @@ u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sam
        return 0;
 }
 
-u64 perf_evsel__intval(struct evsel *evsel, struct perf_sample *sample,
-                      const char *name)
+u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *name)
 {
-       struct tep_format_field *field = perf_evsel__field(evsel, name);
+       struct tep_format_field *field = evsel__field(evsel, name);
 
        if (!field)
                return 0;
@@ -2416,8 +2381,7 @@ u64 perf_evsel__intval(struct evsel *evsel, struct perf_sample *sample,
        return field ? format_field__intval(field, sample, evsel->needs_swap) : 0;
 }
 
-bool perf_evsel__fallback(struct evsel *evsel, int err,
-                         char *msg, size_t msgsize)
+bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize)
 {
        int paranoid;
 
@@ -2442,10 +2406,14 @@ bool perf_evsel__fallback(struct evsel *evsel, int err,
                return true;
        } else if (err == EACCES && !evsel->core.attr.exclude_kernel &&
                   (paranoid = perf_event_paranoid()) > 1) {
-               const char *name = perf_evsel__name(evsel);
+               const char *name = evsel__name(evsel);
                char *new_name;
                const char *sep = ":";
 
+               /* If event has exclude user then don't exclude kernel. */
+               if (evsel->core.attr.exclude_user)
+                       return false;
+
                /* Is there already the separator in the name. */
                if (strchr(name, '/') ||
                    strchr(name, ':'))
@@ -2505,8 +2473,8 @@ static bool find_process(const char *name)
        return ret ? false : true;
 }
 
-int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
-                             int err, char *msg, size_t size)
+int evsel__open_strerror(struct evsel *evsel, struct target *target,
+                        int err, char *msg, size_t size)
 {
        char sbuf[STRERR_BUFSIZE];
        int printed = 0;
@@ -2516,28 +2484,26 @@ int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
        case EACCES:
                if (err == EPERM)
                        printed = scnprintf(msg, size,
-                               "No permission to enable %s event.\n\n",
-                               perf_evsel__name(evsel));
+                               "No permission to enable %s event.\n\n", evsel__name(evsel));
 
                return scnprintf(msg + printed, size - printed,
                 "You may not have permission to collect %sstats.\n\n"
                 "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"
                 "which controls use of the performance events system by\n"
-                "unprivileged users (without CAP_SYS_ADMIN).\n\n"
+                "unprivileged users (without CAP_PERFMON or CAP_SYS_ADMIN).\n\n"
                 "The current value is %d:\n\n"
                 "  -1: Allow use of (almost) all events by all users\n"
                 "      Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK\n"
-                ">= 0: Disallow ftrace function tracepoint by users without CAP_SYS_ADMIN\n"
-                "      Disallow raw tracepoint access by users without CAP_SYS_ADMIN\n"
-                ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n"
-                ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN\n\n"
+                ">= 0: Disallow ftrace function tracepoint by users without CAP_PERFMON or CAP_SYS_ADMIN\n"
+                "      Disallow raw tracepoint access by users without CAP_SYS_PERFMON or CAP_SYS_ADMIN\n"
+                ">= 1: Disallow CPU event access by users without CAP_PERFMON or CAP_SYS_ADMIN\n"
+                ">= 2: Disallow kernel profiling by users without CAP_PERFMON or CAP_SYS_ADMIN\n\n"
                 "To make this setting permanent, edit /etc/sysctl.conf too, e.g.:\n\n"
                 "      kernel.perf_event_paranoid = -1\n" ,
                                 target->system_wide ? "system-wide " : "",
                                 perf_event_paranoid());
        case ENOENT:
-               return scnprintf(msg, size, "The %s event is not supported.",
-                                perf_evsel__name(evsel));
+               return scnprintf(msg, size, "The %s event is not supported.", evsel__name(evsel));
        case EMFILE:
                return scnprintf(msg, size, "%s",
                         "Too many events are opened.\n"
@@ -2561,7 +2527,7 @@ int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
                if (evsel->core.attr.sample_period != 0)
                        return scnprintf(msg, size,
        "%s: PMU Hardware doesn't support sampling/overflow-interrupts. Try 'perf stat'",
-                                        perf_evsel__name(evsel));
+                                        evsel__name(evsel));
                if (evsel->core.attr.precise_ip)
                        return scnprintf(msg, size, "%s",
        "\'precise\' request may not be supported. Try removing 'p' modifier.");
@@ -2594,11 +2560,10 @@ int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
        return scnprintf(msg, size,
        "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"
        "/bin/dmesg | grep -i perf may provide additional information.\n",
-                        err, str_error_r(err, sbuf, sizeof(sbuf)),
-                        perf_evsel__name(evsel));
+                        err, str_error_r(err, sbuf, sizeof(sbuf)), evsel__name(evsel));
 }
 
-struct perf_env *perf_evsel__env(struct evsel *evsel)
+struct perf_env *evsel__env(struct evsel *evsel)
 {
        if (evsel && evsel->evlist)
                return evsel->evlist->env;
@@ -2623,7 +2588,7 @@ static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist)
        return 0;
 }
 
-int perf_evsel__store_ids(struct evsel *evsel, struct evlist *evlist)
+int evsel__store_ids(struct evsel *evsel, struct evlist *evlist)
 {
        struct perf_cpu_map *cpus = evsel->core.cpus;
        struct perf_thread_map *threads = evsel->core.threads;
index 53187c501ee8623e255ebb50b2ab7d2b559cf2e6..351c0aaf2a11874eb653024cfbe75ee6e88b4282 100644 (file)
@@ -18,7 +18,7 @@ struct perf_counts;
 struct perf_stat_evsel;
 union perf_event;
 
-typedef int (perf_evsel__sb_cb_t)(union perf_event *event, void *data);
+typedef int (evsel__sb_cb_t)(union perf_event *event, void *data);
 
 enum perf_tool_event {
        PERF_TOOL_NONE          = 0,
@@ -101,9 +101,17 @@ struct evsel {
        int                     cpu_iter;
        const char              *pmu_name;
        struct {
-               perf_evsel__sb_cb_t     *cb;
-               void                    *data;
+               evsel__sb_cb_t  *cb;
+               void            *data;
        } side_band;
+       /*
+        * For reporting purposes, an evsel sample can have a callchain
+        * synthesized from AUX area data. Keep track of synthesized sample
+        * types here. Note, the recorded sample_type cannot be changed because
+        * it is needed to continue to parse events.
+        * See also evsel__has_callchain().
+        */
+       __u64                   synth_sample_type;
 };
 
 struct perf_missing_features {
@@ -135,7 +143,7 @@ static inline struct perf_cpu_map *evsel__cpus(struct evsel *evsel)
        return perf_evsel__cpus(&evsel->core);
 }
 
-static inline int perf_evsel__nr_cpus(struct evsel *evsel)
+static inline int evsel__nr_cpus(struct evsel *evsel)
 {
        return evsel__cpus(evsel)->nr;
 }
@@ -143,13 +151,16 @@ static inline int perf_evsel__nr_cpus(struct evsel *evsel)
 void perf_counts_values__scale(struct perf_counts_values *count,
                               bool scale, s8 *pscaled);
 
-void perf_evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
-                               struct perf_counts_values *count);
+void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
+                          struct perf_counts_values *count);
 
 int perf_evsel__object_config(size_t object_size,
                              int (*init)(struct evsel *evsel),
                              void (*fini)(struct evsel *evsel));
 
+struct perf_pmu *evsel__find_pmu(struct evsel *evsel);
+bool evsel__is_aux_event(struct evsel *evsel);
+
 struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
 
 static inline struct evsel *evsel__new(struct perf_event_attr *attr)
@@ -172,22 +183,20 @@ struct evsel *perf_evsel__new_cycles(bool precise);
 struct tep_event *event_format__new(const char *sys, const char *name);
 
 void evsel__init(struct evsel *evsel, struct perf_event_attr *attr, int idx);
-void perf_evsel__exit(struct evsel *evsel);
+void evsel__exit(struct evsel *evsel);
 void evsel__delete(struct evsel *evsel);
 
 struct callchain_param;
 
-void perf_evsel__config(struct evsel *evsel,
-                       struct record_opts *opts,
-                       struct callchain_param *callchain);
-void perf_evsel__config_callchain(struct evsel *evsel,
-                                 struct record_opts *opts,
-                                 struct callchain_param *callchain);
+void evsel__config(struct evsel *evsel, struct record_opts *opts,
+                  struct callchain_param *callchain);
+void evsel__config_callchain(struct evsel *evsel, struct record_opts *opts,
+                            struct callchain_param *callchain);
 
-int __perf_evsel__sample_size(u64 sample_type);
-void perf_evsel__calc_id_pos(struct evsel *evsel);
+int __evsel__sample_size(u64 sample_type);
+void evsel__calc_id_pos(struct evsel *evsel);
 
-bool perf_evsel__is_cache_op_valid(u8 type, u8 op);
+bool evsel__is_cache_op_valid(u8 type, u8 op);
 
 #define PERF_EVSEL__MAX_ALIASES 8
 
@@ -199,177 +208,153 @@ extern const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
                                              [PERF_EVSEL__MAX_ALIASES];
 extern const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX];
 extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
-int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
-                                           char *bf, size_t size);
-const char *perf_evsel__name(struct evsel *evsel);
+int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size);
+const char *evsel__name(struct evsel *evsel);
 
-const char *perf_evsel__group_name(struct evsel *evsel);
-int perf_evsel__group_desc(struct evsel *evsel, char *buf, size_t size);
+const char *evsel__group_name(struct evsel *evsel);
+int evsel__group_desc(struct evsel *evsel, char *buf, size_t size);
 
-void __perf_evsel__set_sample_bit(struct evsel *evsel,
-                                 enum perf_event_sample_format bit);
-void __perf_evsel__reset_sample_bit(struct evsel *evsel,
-                                   enum perf_event_sample_format bit);
+void __evsel__set_sample_bit(struct evsel *evsel, enum perf_event_sample_format bit);
+void __evsel__reset_sample_bit(struct evsel *evsel, enum perf_event_sample_format bit);
 
-#define perf_evsel__set_sample_bit(evsel, bit) \
-       __perf_evsel__set_sample_bit(evsel, PERF_SAMPLE_##bit)
+#define evsel__set_sample_bit(evsel, bit) \
+       __evsel__set_sample_bit(evsel, PERF_SAMPLE_##bit)
 
-#define perf_evsel__reset_sample_bit(evsel, bit) \
-       __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit)
+#define evsel__reset_sample_bit(evsel, bit) \
+       __evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit)
 
-void perf_evsel__set_sample_id(struct evsel *evsel,
-                              bool use_sample_identifier);
+void evsel__set_sample_id(struct evsel *evsel, bool use_sample_identifier);
 
-int perf_evsel__set_filter(struct evsel *evsel, const char *filter);
-int perf_evsel__append_tp_filter(struct evsel *evsel, const char *filter);
-int perf_evsel__append_addr_filter(struct evsel *evsel,
-                                  const char *filter);
+int evsel__set_filter(struct evsel *evsel, const char *filter);
+int evsel__append_tp_filter(struct evsel *evsel, const char *filter);
+int evsel__append_addr_filter(struct evsel *evsel, const char *filter);
 int evsel__enable_cpu(struct evsel *evsel, int cpu);
 int evsel__enable(struct evsel *evsel);
 int evsel__disable(struct evsel *evsel);
 int evsel__disable_cpu(struct evsel *evsel, int cpu);
 
-int perf_evsel__open_per_cpu(struct evsel *evsel,
-                            struct perf_cpu_map *cpus,
-                            int cpu);
-int perf_evsel__open_per_thread(struct evsel *evsel,
-                               struct perf_thread_map *threads);
+int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu);
+int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads);
 int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus,
                struct perf_thread_map *threads);
 void evsel__close(struct evsel *evsel);
 
 struct perf_sample;
 
-void *perf_evsel__rawptr(struct evsel *evsel, struct perf_sample *sample,
-                        const char *name);
-u64 perf_evsel__intval(struct evsel *evsel, struct perf_sample *sample,
-                      const char *name);
+void *evsel__rawptr(struct evsel *evsel, struct perf_sample *sample, const char *name);
+u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *name);
 
-static inline char *perf_evsel__strval(struct evsel *evsel,
-                                      struct perf_sample *sample,
-                                      const char *name)
+static inline char *evsel__strval(struct evsel *evsel, struct perf_sample *sample, const char *name)
 {
-       return perf_evsel__rawptr(evsel, sample, name);
+       return evsel__rawptr(evsel, sample, name);
 }
 
 struct tep_format_field;
 
 u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, bool needs_swap);
 
-struct tep_format_field *perf_evsel__field(struct evsel *evsel, const char *name);
+struct tep_format_field *evsel__field(struct evsel *evsel, const char *name);
 
-#define perf_evsel__match(evsel, t, c)         \
+#define evsel__match(evsel, t, c)              \
        (evsel->core.attr.type == PERF_TYPE_##t &&      \
         evsel->core.attr.config == PERF_COUNT_##c)
 
-static inline bool perf_evsel__match2(struct evsel *e1,
-                                     struct evsel *e2)
+static inline bool evsel__match2(struct evsel *e1, struct evsel *e2)
 {
        return (e1->core.attr.type == e2->core.attr.type) &&
               (e1->core.attr.config == e2->core.attr.config);
 }
 
-#define perf_evsel__cmp(a, b)                  \
-       ((a) &&                                 \
-        (b) &&                                 \
-        (a)->core.attr.type == (b)->core.attr.type &&  \
-        (a)->core.attr.config == (b)->core.attr.config)
-
-int perf_evsel__read_counter(struct evsel *evsel, int cpu, int thread);
+int evsel__read_counter(struct evsel *evsel, int cpu, int thread);
 
-int __perf_evsel__read_on_cpu(struct evsel *evsel,
-                             int cpu, int thread, bool scale);
+int __evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread, bool scale);
 
 /**
- * perf_evsel__read_on_cpu - Read out the results on a CPU and thread
+ * evsel__read_on_cpu - Read out the results on a CPU and thread
  *
  * @evsel - event selector to read value
  * @cpu - CPU of interest
  * @thread - thread of interest
  */
-static inline int perf_evsel__read_on_cpu(struct evsel *evsel,
-                                         int cpu, int thread)
+static inline int evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread)
 {
-       return __perf_evsel__read_on_cpu(evsel, cpu, thread, false);
+       return __evsel__read_on_cpu(evsel, cpu, thread, false);
 }
 
 /**
- * perf_evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled
+ * evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled
  *
  * @evsel - event selector to read value
  * @cpu - CPU of interest
  * @thread - thread of interest
  */
-static inline int perf_evsel__read_on_cpu_scaled(struct evsel *evsel,
-                                                int cpu, int thread)
+static inline int evsel__read_on_cpu_scaled(struct evsel *evsel, int cpu, int thread)
 {
-       return __perf_evsel__read_on_cpu(evsel, cpu, thread, true);
+       return __evsel__read_on_cpu(evsel, cpu, thread, true);
 }
 
-int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event,
-                            struct perf_sample *sample);
+int evsel__parse_sample(struct evsel *evsel, union perf_event *event,
+                       struct perf_sample *sample);
 
-int perf_evsel__parse_sample_timestamp(struct evsel *evsel,
-                                      union perf_event *event,
-                                      u64 *timestamp);
+int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event,
+                                 u64 *timestamp);
 
-static inline struct evsel *perf_evsel__next(struct evsel *evsel)
+static inline struct evsel *evsel__next(struct evsel *evsel)
 {
        return list_entry(evsel->core.node.next, struct evsel, core.node);
 }
 
-static inline struct evsel *perf_evsel__prev(struct evsel *evsel)
+static inline struct evsel *evsel__prev(struct evsel *evsel)
 {
        return list_entry(evsel->core.node.prev, struct evsel, core.node);
 }
 
 /**
- * perf_evsel__is_group_leader - Return whether given evsel is a leader event
+ * evsel__is_group_leader - Return whether given evsel is a leader event
  *
  * @evsel - evsel selector to be tested
  *
  * Return %true if @evsel is a group leader or a stand-alone event
  */
-static inline bool perf_evsel__is_group_leader(const struct evsel *evsel)
+static inline bool evsel__is_group_leader(const struct evsel *evsel)
 {
        return evsel->leader == evsel;
 }
 
 /**
- * perf_evsel__is_group_event - Return whether given evsel is a group event
+ * evsel__is_group_event - Return whether given evsel is a group event
  *
  * @evsel - evsel selector to be tested
  *
  * Return %true iff event group view is enabled and @evsel is a actual group
  * leader which has other members in the group
  */
-static inline bool perf_evsel__is_group_event(struct evsel *evsel)
+static inline bool evsel__is_group_event(struct evsel *evsel)
 {
        if (!symbol_conf.event_group)
                return false;
 
-       return perf_evsel__is_group_leader(evsel) && evsel->core.nr_members > 1;
+       return evsel__is_group_leader(evsel) && evsel->core.nr_members > 1;
 }
 
-bool perf_evsel__is_function_event(struct evsel *evsel);
+bool evsel__is_function_event(struct evsel *evsel);
 
-static inline bool perf_evsel__is_bpf_output(struct evsel *evsel)
+static inline bool evsel__is_bpf_output(struct evsel *evsel)
 {
-       return perf_evsel__match(evsel, SOFTWARE, SW_BPF_OUTPUT);
+       return evsel__match(evsel, SOFTWARE, SW_BPF_OUTPUT);
 }
 
-static inline bool perf_evsel__is_clock(struct evsel *evsel)
+static inline bool evsel__is_clock(struct evsel *evsel)
 {
-       return perf_evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
-              perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK);
+       return evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
+              evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK);
 }
 
-bool perf_evsel__fallback(struct evsel *evsel, int err,
-                         char *msg, size_t msgsize);
-int perf_evsel__open_strerror(struct evsel *evsel, struct target *target,
-                             int err, char *msg, size_t size);
+bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize);
+int evsel__open_strerror(struct evsel *evsel, struct target *target,
+                        int err, char *msg, size_t size);
 
-static inline int perf_evsel__group_idx(struct evsel *evsel)
+static inline int evsel__group_idx(struct evsel *evsel)
 {
        return evsel->idx - evsel->leader->idx;
 }
@@ -386,22 +371,37 @@ for ((_evsel) = _leader;                                                  \
      (_evsel) && (_evsel)->leader == (_leader);                                        \
      (_evsel) = list_entry((_evsel)->core.node.next, struct evsel, core.node))
 
-static inline bool perf_evsel__has_branch_callstack(const struct evsel *evsel)
+static inline bool evsel__has_branch_callstack(const struct evsel *evsel)
 {
        return evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK;
 }
 
-static inline bool perf_evsel__has_branch_hw_idx(const struct evsel *evsel)
+static inline bool evsel__has_branch_hw_idx(const struct evsel *evsel)
 {
        return evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX;
 }
 
 static inline bool evsel__has_callchain(const struct evsel *evsel)
 {
-       return (evsel->core.attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0;
+       /*
+        * For reporting purposes, an evsel sample can have a recorded callchain
+        * or a callchain synthesized from AUX area data.
+        */
+       return evsel->core.attr.sample_type & PERF_SAMPLE_CALLCHAIN ||
+              evsel->synth_sample_type & PERF_SAMPLE_CALLCHAIN;
+}
+
+static inline bool evsel__has_br_stack(const struct evsel *evsel)
+{
+       /*
+        * For reporting purposes, an evsel sample can have a recorded branch
+        * stack or a branch stack synthesized from AUX area data.
+        */
+       return evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_STACK ||
+              evsel->synth_sample_type & PERF_SAMPLE_BRANCH_STACK;
 }
 
-struct perf_env *perf_evsel__env(struct evsel *evsel);
+struct perf_env *evsel__env(struct evsel *evsel);
 
-int perf_evsel__store_ids(struct evsel *evsel, struct evlist *evlist);
+int evsel__store_ids(struct evsel *evsel, struct evlist *evlist);
 #endif /* __PERF_EVSEL_H */
index e026ab67b00859c5fc1d15d2db691d31cce3b572..f8938916577c028ae44ceab49eaa6ed04f01b52b 100644 (file)
@@ -7,7 +7,7 @@
 
 /*
  * The 'struct perf_evsel_config_term' is used to pass event
- * specific configuration data to perf_evsel__config routine.
+ * specific configuration data to evsel__config routine.
  * It is allocated within event parsing and attached to
  * perf_evsel::config_terms list head.
 */
index 3b4842840db0f62e5a306e85820374cee1b8a9a7..99aed708bd5aa28a48f1f1bc607e04a6af8333c3 100644 (file)
@@ -44,22 +44,22 @@ int perf_evsel__fprintf(struct evsel *evsel,
        if (details->event_group) {
                struct evsel *pos;
 
-               if (!perf_evsel__is_group_leader(evsel))
+               if (!evsel__is_group_leader(evsel))
                        return 0;
 
                if (evsel->core.nr_members > 1)
                        printed += fprintf(fp, "%s{", evsel->group_name ?: "");
 
-               printed += fprintf(fp, "%s", perf_evsel__name(evsel));
+               printed += fprintf(fp, "%s", evsel__name(evsel));
                for_each_group_member(pos, evsel)
-                       printed += fprintf(fp, ",%s", perf_evsel__name(pos));
+                       printed += fprintf(fp, ",%s", evsel__name(pos));
 
                if (evsel->core.nr_members > 1)
                        printed += fprintf(fp, "}");
                goto out;
        }
 
-       printed += fprintf(fp, "%s", perf_evsel__name(evsel));
+       printed += fprintf(fp, "%s", evsel__name(evsel));
 
        if (details->verbose) {
                printed += perf_event_attr__fprintf(fp, &evsel->core.attr,
index fd192ddf93c16c6200fe6ae98275950f8975fdb9..aa631e37ad1e85710b4b3075afb7ab070ebfd3f3 100644 (file)
@@ -3,7 +3,6 @@
 #include <assert.h>
 #include "expr.h"
 #include "expr-bison.h"
-#define YY_EXTRA_TYPE int
 #include "expr-flex.h"
 
 #ifdef PARSER_DEBUG
@@ -11,7 +10,7 @@ extern int expr_debug;
 #endif
 
 /* Caller must make sure id is allocated */
-void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
+void expr__add_id(struct expr_parse_ctx *ctx, const char *name, double val)
 {
        int idx;
 
@@ -21,20 +20,24 @@ void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
        ctx->ids[idx].val = val;
 }
 
-void expr__ctx_init(struct parse_ctx *ctx)
+void expr__ctx_init(struct expr_parse_ctx *ctx)
 {
        ctx->num_ids = 0;
 }
 
 static int
-__expr__parse(double *val, struct parse_ctx *ctx, const char *expr,
-             int start)
+__expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
+             int start, int runtime)
 {
+       struct expr_scanner_ctx scanner_ctx = {
+               .start_token = start,
+               .runtime = runtime,
+       };
        YY_BUFFER_STATE buffer;
        void *scanner;
        int ret;
 
-       ret = expr_lex_init_extra(start, &scanner);
+       ret = expr_lex_init_extra(&scanner_ctx, &scanner);
        if (ret)
                return ret;
 
@@ -52,9 +55,9 @@ __expr__parse(double *val, struct parse_ctx *ctx, const char *expr,
        return ret;
 }
 
-int expr__parse(double *final_val, struct parse_ctx *ctx, const char *expr)
+int expr__parse(double *final_val, struct expr_parse_ctx *ctx, const char *expr, int runtime)
 {
-       return __expr__parse(final_val, ctx, expr, EXPR_PARSE) ? -1 : 0;
+       return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 0;
 }
 
 static bool
@@ -72,13 +75,13 @@ already_seen(const char *val, const char *one, const char **other,
 }
 
 int expr__find_other(const char *expr, const char *one, const char ***other,
-                    int *num_other)
+                    int *num_other, int runtime)
 {
        int err, i = 0, j = 0;
-       struct parse_ctx ctx;
+       struct expr_parse_ctx ctx;
 
        expr__ctx_init(&ctx);
-       err = __expr__parse(NULL, &ctx, expr, EXPR_OTHER);
+       err = __expr__parse(NULL, &ctx, expr, EXPR_OTHER, runtime);
        if (err)
                return -1;
 
index 9377538f40978170125d44b8cf1ea4be9cad3564..87d627bb699b4ce67f19f311acbf45bcdef5b852 100644 (file)
@@ -5,20 +5,25 @@
 #define EXPR_MAX_OTHER 20
 #define MAX_PARSE_ID EXPR_MAX_OTHER
 
-struct parse_id {
+struct expr_parse_id {
        const char *name;
        double val;
 };
 
-struct parse_ctx {
+struct expr_parse_ctx {
        int num_ids;
-       struct parse_id ids[MAX_PARSE_ID];
+       struct expr_parse_id ids[MAX_PARSE_ID];
 };
 
-void expr__ctx_init(struct parse_ctx *ctx);
-void expr__add_id(struct parse_ctx *ctx, const char *id, double val);
-int expr__parse(double *final_val, struct parse_ctx *ctx, const char *expr);
+struct expr_scanner_ctx {
+       int start_token;
+       int runtime;
+};
+
+void expr__ctx_init(struct expr_parse_ctx *ctx);
+void expr__add_id(struct expr_parse_ctx *ctx, const char *id, double val);
+int expr__parse(double *final_val, struct expr_parse_ctx *ctx, const char *expr, int runtime);
 int expr__find_other(const char *expr, const char *one, const char ***other,
-               int *num_other);
+               int *num_other, int runtime);
 
 #endif
index eaad29243c23b9102b0214747532221a43999927..74b9b59b1aa5c8508639c3e47d2fe2dd0dcb7f12 100644 (file)
@@ -35,7 +35,7 @@ static int value(yyscan_t scanner, int base)
  * Allow @ instead of / to be able to specify pmu/event/ without
  * conflicts with normal division.
  */
-static char *normalize(char *str)
+static char *normalize(char *str, int runtime)
 {
        char *ret = str;
        char *dst = str;
@@ -45,6 +45,19 @@ static char *normalize(char *str)
                        *dst++ = '/';
                else if (*str == '\\')
                        *dst++ = *++str;
+                else if (*str == '?') {
+                       char *paramval;
+                       int i = 0;
+                       int size = asprintf(&paramval, "%d", runtime);
+
+                       if (size < 0)
+                               *dst++ = '0';
+                       else {
+                               while (i < size)
+                                       *dst++ = paramval[i++];
+                               free(paramval);
+                       }
+               }
                else
                        *dst++ = *str;
                str++;
@@ -54,16 +67,16 @@ static char *normalize(char *str)
        return ret;
 }
 
-static int str(yyscan_t scanner, int token)
+static int str(yyscan_t scanner, int token, int runtime)
 {
        YYSTYPE *yylval = expr_get_lval(scanner);
        char *text = expr_get_text(scanner);
 
-       yylval->str = normalize(strdup(text));
+       yylval->str = normalize(strdup(text), runtime);
        if (!yylval->str)
                return EXPR_ERROR;
 
-       yylval->str = normalize(yylval->str);
+       yylval->str = normalize(yylval->str, runtime);
        return token;
 }
 %}
@@ -72,17 +85,17 @@ number              [0-9]+
 
 sch            [-,=]
 spec           \\{sch}
-sym            [0-9a-zA-Z_\.:@]+
-symbol         {spec}*{sym}*{spec}*{sym}*
+sym            [0-9a-zA-Z_\.:@?]+
+symbol         {spec}*{sym}*{spec}*{sym}*{spec}*{sym}
 
 %%
-       {
-               int start_token;
+       struct expr_scanner_ctx *sctx = expr_get_extra(yyscanner);
 
-               start_token = expr_get_extra(yyscanner);
+       {
+               int start_token = sctx->start_token;
 
-               if (start_token) {
-                       expr_set_extra(NULL, yyscanner);
+               if (sctx->start_token) {
+                       sctx->start_token = 0;
                        return start_token;
                }
        }
@@ -93,7 +106,7 @@ if           { return IF; }
 else           { return ELSE; }
 #smt_on                { return SMT_ON; }
 {number}       { return value(yyscanner, 10); }
-{symbol}       { return str(yyscanner, ID); }
+{symbol}       { return str(yyscanner, ID, sctx->runtime); }
 "|"            { return '|'; }
 "^"            { return '^'; }
 "&"            { return '&'; }
index 4720cbe793571632e6b2a6b5b1f8a210af9383ca..cd17486c1c5d0423b383c35a5cb66f50d290ce81 100644 (file)
@@ -15,7 +15,7 @@
 %define api.pure full
 
 %parse-param { double *final_val }
-%parse-param { struct parse_ctx *ctx }
+%parse-param { struct expr_parse_ctx *ctx }
 %parse-param {void *scanner}
 %lex-param {void* scanner}
 
 
 %{
 static void expr_error(double *final_val __maybe_unused,
-                      struct parse_ctx *ctx __maybe_unused,
+                      struct expr_parse_ctx *ctx __maybe_unused,
                       void *scanner,
                       const char *s)
 {
        pr_debug("%s\n", s);
 }
 
-static int lookup_id(struct parse_ctx *ctx, char *id, double *val)
+static int lookup_id(struct expr_parse_ctx *ctx, char *id, double *val)
 {
        int i;
 
index acbd046bf95c3929991ca67c77a2475df3d4bf64..0ce47283a8a133e1b64218cc5b437f7fb96f9cc8 100644 (file)
@@ -525,7 +525,7 @@ static int write_event_desc(struct feat_fd *ff,
                /*
                 * write event string as passed on cmdline
                 */
-               ret = do_write_string(ff, perf_evsel__name(evsel));
+               ret = do_write_string(ff, evsel__name(evsel));
                if (ret < 0)
                        return ret;
                /*
@@ -783,8 +783,7 @@ static int write_group_desc(struct feat_fd *ff,
                return ret;
 
        evlist__for_each_entry(evlist, evsel) {
-               if (perf_evsel__is_group_leader(evsel) &&
-                   evsel->core.nr_members > 1) {
+               if (evsel__is_group_leader(evsel) && evsel->core.nr_members > 1) {
                        const char *name = evsel->group_name ?: "{anon_group}";
                        u32 leader_idx = evsel->idx;
                        u32 nr_members = evsel->core.nr_members;
@@ -1395,6 +1394,38 @@ static int write_compressed(struct feat_fd *ff __maybe_unused,
        return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
 }
 
+static int write_cpu_pmu_caps(struct feat_fd *ff,
+                             struct evlist *evlist __maybe_unused)
+{
+       struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
+       struct perf_pmu_caps *caps = NULL;
+       int nr_caps;
+       int ret;
+
+       if (!cpu_pmu)
+               return -ENOENT;
+
+       nr_caps = perf_pmu__caps_parse(cpu_pmu);
+       if (nr_caps < 0)
+               return nr_caps;
+
+       ret = do_write(ff, &nr_caps, sizeof(nr_caps));
+       if (ret < 0)
+               return ret;
+
+       list_for_each_entry(caps, &cpu_pmu->caps, list) {
+               ret = do_write_string(ff, caps->name);
+               if (ret < 0)
+                       return ret;
+
+               ret = do_write_string(ff, caps->value);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
 static void print_hostname(struct feat_fd *ff, FILE *fp)
 {
        fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
@@ -1809,6 +1840,27 @@ static void print_compressed(struct feat_fd *ff, FILE *fp)
                ff->ph->env.comp_level, ff->ph->env.comp_ratio);
 }
 
+static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
+{
+       const char *delimiter = "# cpu pmu capabilities: ";
+       u32 nr_caps = ff->ph->env.nr_cpu_pmu_caps;
+       char *str;
+
+       if (!nr_caps) {
+               fprintf(fp, "# cpu pmu capabilities: not available\n");
+               return;
+       }
+
+       str = ff->ph->env.cpu_pmu_caps;
+       while (nr_caps--) {
+               fprintf(fp, "%s%s", delimiter, str);
+               delimiter = ", ";
+               str += strlen(str) + 1;
+       }
+
+       fprintf(fp, "\n");
+}
+
 static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
 {
        const char *delimiter = "# pmu mappings: ";
@@ -1854,14 +1906,12 @@ static void print_group_desc(struct feat_fd *ff, FILE *fp)
        session = container_of(ff->ph, struct perf_session, header);
 
        evlist__for_each_entry(session->evlist, evsel) {
-               if (perf_evsel__is_group_leader(evsel) &&
-                   evsel->core.nr_members > 1) {
-                       fprintf(fp, "# group: %s{%s", evsel->group_name ?: "",
-                               perf_evsel__name(evsel));
+               if (evsel__is_group_leader(evsel) && evsel->core.nr_members > 1) {
+                       fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", evsel__name(evsel));
 
                        nr = evsel->core.nr_members - 1;
                } else if (nr) {
-                       fprintf(fp, ",%s", perf_evsel__name(evsel));
+                       fprintf(fp, ",%s", evsel__name(evsel));
 
                        if (--nr == 0)
                                fprintf(fp, "}\n");
@@ -2846,6 +2896,60 @@ static int process_compressed(struct feat_fd *ff,
        return 0;
 }
 
+static int process_cpu_pmu_caps(struct feat_fd *ff,
+                               void *data __maybe_unused)
+{
+       char *name, *value;
+       struct strbuf sb;
+       u32 nr_caps;
+
+       if (do_read_u32(ff, &nr_caps))
+               return -1;
+
+       if (!nr_caps) {
+               pr_debug("cpu pmu capabilities not available\n");
+               return 0;
+       }
+
+       ff->ph->env.nr_cpu_pmu_caps = nr_caps;
+
+       if (strbuf_init(&sb, 128) < 0)
+               return -1;
+
+       while (nr_caps--) {
+               name = do_read_string(ff);
+               if (!name)
+                       goto error;
+
+               value = do_read_string(ff);
+               if (!value)
+                       goto free_name;
+
+               if (strbuf_addf(&sb, "%s=%s", name, value) < 0)
+                       goto free_value;
+
+               /* include a NULL character at the end */
+               if (strbuf_add(&sb, "", 1) < 0)
+                       goto free_value;
+
+               if (!strcmp(name, "branches"))
+                       ff->ph->env.max_branches = atoi(value);
+
+               free(value);
+               free(name);
+       }
+       ff->ph->env.cpu_pmu_caps = strbuf_detach(&sb, NULL);
+       return 0;
+
+free_value:
+       free(value);
+free_name:
+       free(name);
+error:
+       strbuf_release(&sb);
+       return -1;
+}
+
 #define FEAT_OPR(n, func, __full_only) \
        [HEADER_##n] = {                                        \
                .name       = __stringify(n),                   \
@@ -2903,6 +3007,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
        FEAT_OPR(BPF_PROG_INFO, bpf_prog_info,  false),
        FEAT_OPR(BPF_BTF,       bpf_btf,        false),
        FEAT_OPR(COMPRESSED,    compressed,     false),
+       FEAT_OPR(CPU_PMU_CAPS,  cpu_pmu_caps,   false),
 };
 
 struct header_print_data {
index 840f95cee349407f53d2d9842aebd09389220f98..650bd1c7a99b9e809d5b60bb43bb4ffd3229017f 100644 (file)
@@ -43,6 +43,7 @@ enum {
        HEADER_BPF_PROG_INFO,
        HEADER_BPF_BTF,
        HEADER_COMPRESSED,
+       HEADER_CPU_PMU_CAPS,
        HEADER_LAST_FEATURE,
        HEADER_FEAT_BITS        = 256,
 };
index 283a69ff6a3dcf941506cbbd7f4a5ed9e7ec7dd0..12b65d00cf659e561445514f2be8004c48b69f6d 100644 (file)
@@ -1070,6 +1070,20 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter,
        return fill_callchain_info(al, node, iter->hide_unresolved);
 }
 
+static bool
+hist_entry__fast__sym_diff(struct hist_entry *left,
+                          struct hist_entry *right)
+{
+       struct symbol *sym_l = left->ms.sym;
+       struct symbol *sym_r = right->ms.sym;
+
+       if (!sym_l && !sym_r)
+               return left->ip != right->ip;
+
+       return !!_sort__sym_cmp(sym_l, sym_r);
+}
+
+
 static int
 iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
                               struct addr_location *al)
@@ -1096,6 +1110,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
        };
        int i;
        struct callchain_cursor cursor;
+       bool fast = hists__has(he_tmp.hists, sym);
 
        callchain_cursor_snapshot(&cursor, &callchain_cursor);
 
@@ -1106,6 +1121,14 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
         * It's possible that it has cycles or recursive calls.
         */
        for (i = 0; i < iter->curr; i++) {
+               /*
+                * For most cases, there are no duplicate entries in callchain.
+                * The symbols are usually different. Do a quick check for
+                * symbols first.
+                */
+               if (fast && hist_entry__fast__sym_diff(he_cache[i], &he_tmp))
+                       continue;
+
                if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
                        /* to avoid calling callback function */
                        iter->he = NULL;
@@ -2637,7 +2660,7 @@ size_t perf_evlist__fprintf_nr_events(struct evlist *evlist, FILE *fp)
        size_t ret = 0;
 
        evlist__for_each_entry(evlist, pos) {
-               ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
+               ret += fprintf(fp, "%s stats:\n", evsel__name(pos));
                ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp);
        }
 
@@ -2661,7 +2684,7 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh
        unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
        u64 nr_events = hists->stats.total_period;
        struct evsel *evsel = hists_to_evsel(hists);
-       const char *ev_name = perf_evsel__name(evsel);
+       const char *ev_name = evsel__name(evsel);
        char buf[512], sample_freq_str[64] = "";
        size_t buflen = sizeof(buf);
        char ref[30] = " show reference callgraph, ";
@@ -2672,10 +2695,10 @@ int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool sh
                nr_events = hists->stats.total_non_filtered_period;
        }
 
-       if (perf_evsel__is_group_event(evsel)) {
+       if (evsel__is_group_event(evsel)) {
                struct evsel *pos;
 
-               perf_evsel__group_desc(evsel, buf, buflen);
+               evsel__group_desc(evsel, buf, buflen);
                ev_name = buf;
 
                for_each_group_member(pos, evsel) {
index 34cb380d19a34238677c1c187fb1336bfe49d90c..af1e78d76228d88ffd7ca07bde719a930d35f48f 100644 (file)
@@ -432,7 +432,7 @@ static int intel_bts_process_buffer(struct intel_bts_queue *btsq,
                                            le64_to_cpu(branch->from),
                                            le64_to_cpu(branch->to),
                                            btsq->intel_pt_insn.length,
-                                           buffer->buffer_nr + 1);
+                                           buffer->buffer_nr + 1, true, 0, 0);
                if (filter && !(filter & btsq->sample_flags))
                        continue;
                err = intel_bts_synth_branch_sample(btsq, branch);
@@ -728,6 +728,15 @@ static void intel_bts_free(struct perf_session *session)
        free(bts);
 }
 
+static bool intel_bts_evsel_is_auxtrace(struct perf_session *session,
+                                       struct evsel *evsel)
+{
+       struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts,
+                                            auxtrace);
+
+       return evsel->core.attr.type == bts->pmu_type;
+}
+
 struct intel_bts_synth {
        struct perf_tool dummy_tool;
        struct perf_session *session;
@@ -816,10 +825,10 @@ static int intel_bts_synth_events(struct intel_bts *bts,
                bts->branches_id = id;
                /*
                 * We only use sample types from PERF_SAMPLE_MASK so we can use
-                * __perf_evsel__sample_size() here.
+                * __evsel__sample_size() here.
                 */
                bts->branches_event_size = sizeof(struct perf_record_sample) +
-                               __perf_evsel__sample_size(attr.sample_type);
+                                          __evsel__sample_size(attr.sample_type);
        }
 
        return 0;
@@ -883,6 +892,7 @@ int intel_bts_process_auxtrace_info(union perf_event *event,
        bts->auxtrace.flush_events = intel_bts_flush;
        bts->auxtrace.free_events = intel_bts_free_events;
        bts->auxtrace.free = intel_bts_free;
+       bts->auxtrace.evsel_is_auxtrace = intel_bts_evsel_is_auxtrace;
        session->auxtrace = &bts->auxtrace;
 
        intel_bts_print_info(&auxtrace_info->priv[0], INTEL_BTS_PMU_TYPE,
index 0ccf10a0bf44527124ef28771168bfd53085800b..4ce109993e74e1dad7b690f4bf80d574dda03d75 100644 (file)
@@ -552,7 +552,7 @@ static int intel_pt_do_get_packet(const unsigned char *buf, size_t len,
                break;
        default:
                break;
-       };
+       }
 
        if (!(byte & BIT(0))) {
                if (byte == 0)
index 23c8289c2472d723e7a07413f3705c3c60c3cb94..f17b1e769ae44d37d85c503cac765cdfae1d6eae 100644 (file)
@@ -33,6 +33,7 @@
 #include "tsc.h"
 #include "intel-pt.h"
 #include "config.h"
+#include "util/perf_api_probe.h"
 #include "util/synthetic-events.h"
 #include "time-utils.h"
 
@@ -68,6 +69,10 @@ struct intel_pt {
        bool est_tsc;
        bool sync_switch;
        bool mispred_all;
+       bool use_thread_stack;
+       bool callstack;
+       unsigned int br_stack_sz;
+       unsigned int br_stack_sz_plus;
        int have_sched_switch;
        u32 pmu_type;
        u64 kernel_start;
@@ -124,6 +129,9 @@ struct intel_pt {
 
        struct range *time_ranges;
        unsigned int range_cnt;
+
+       struct ip_callchain *chain;
+       struct branch_stack *br_stack;
 };
 
 enum switch_state {
@@ -143,8 +151,6 @@ struct intel_pt_queue {
        const struct intel_pt_state *state;
        struct ip_callchain *chain;
        struct branch_stack *last_branch;
-       struct branch_stack *last_branch_rb;
-       size_t last_branch_pos;
        union perf_event *event_buf;
        bool on_heap;
        bool stop;
@@ -868,6 +874,83 @@ static u64 intel_pt_ns_to_ticks(const struct intel_pt *pt, u64 ns)
                pt->tc.time_mult;
 }
 
+static struct ip_callchain *intel_pt_alloc_chain(struct intel_pt *pt)
+{
+       size_t sz = sizeof(struct ip_callchain);
+
+       /* Add 1 to callchain_sz for callchain context */
+       sz += (pt->synth_opts.callchain_sz + 1) * sizeof(u64);
+       return zalloc(sz);
+}
+
+static int intel_pt_callchain_init(struct intel_pt *pt)
+{
+       struct evsel *evsel;
+
+       evlist__for_each_entry(pt->session->evlist, evsel) {
+               if (!(evsel->core.attr.sample_type & PERF_SAMPLE_CALLCHAIN))
+                       evsel->synth_sample_type |= PERF_SAMPLE_CALLCHAIN;
+       }
+
+       pt->chain = intel_pt_alloc_chain(pt);
+       if (!pt->chain)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void intel_pt_add_callchain(struct intel_pt *pt,
+                                  struct perf_sample *sample)
+{
+       struct thread *thread = machine__findnew_thread(pt->machine,
+                                                       sample->pid,
+                                                       sample->tid);
+
+       thread_stack__sample_late(thread, sample->cpu, pt->chain,
+                                 pt->synth_opts.callchain_sz + 1, sample->ip,
+                                 pt->kernel_start);
+
+       sample->callchain = pt->chain;
+}
+
+static struct branch_stack *intel_pt_alloc_br_stack(struct intel_pt *pt)
+{
+       size_t sz = sizeof(struct branch_stack);
+
+       sz += pt->br_stack_sz * sizeof(struct branch_entry);
+       return zalloc(sz);
+}
+
+static int intel_pt_br_stack_init(struct intel_pt *pt)
+{
+       struct evsel *evsel;
+
+       evlist__for_each_entry(pt->session->evlist, evsel) {
+               if (!(evsel->core.attr.sample_type & PERF_SAMPLE_BRANCH_STACK))
+                       evsel->synth_sample_type |= PERF_SAMPLE_BRANCH_STACK;
+       }
+
+       pt->br_stack = intel_pt_alloc_br_stack(pt);
+       if (!pt->br_stack)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void intel_pt_add_br_stack(struct intel_pt *pt,
+                                 struct perf_sample *sample)
+{
+       struct thread *thread = machine__findnew_thread(pt->machine,
+                                                       sample->pid,
+                                                       sample->tid);
+
+       thread_stack__br_sample_late(thread, sample->cpu, pt->br_stack,
+                                    pt->br_stack_sz, sample->ip,
+                                    pt->kernel_start);
+
+       sample->branch_stack = pt->br_stack;
+}
+
 static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
                                                   unsigned int queue_nr)
 {
@@ -880,26 +963,15 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
                return NULL;
 
        if (pt->synth_opts.callchain) {
-               size_t sz = sizeof(struct ip_callchain);
-
-               /* Add 1 to callchain_sz for callchain context */
-               sz += (pt->synth_opts.callchain_sz + 1) * sizeof(u64);
-               ptq->chain = zalloc(sz);
+               ptq->chain = intel_pt_alloc_chain(pt);
                if (!ptq->chain)
                        goto out_free;
        }
 
        if (pt->synth_opts.last_branch) {
-               size_t sz = sizeof(struct branch_stack);
-
-               sz += pt->synth_opts.last_branch_sz *
-                     sizeof(struct branch_entry);
-               ptq->last_branch = zalloc(sz);
+               ptq->last_branch = intel_pt_alloc_br_stack(pt);
                if (!ptq->last_branch)
                        goto out_free;
-               ptq->last_branch_rb = zalloc(sz);
-               if (!ptq->last_branch_rb)
-                       goto out_free;
        }
 
        ptq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
@@ -968,7 +1040,6 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt,
 out_free:
        zfree(&ptq->event_buf);
        zfree(&ptq->last_branch);
-       zfree(&ptq->last_branch_rb);
        zfree(&ptq->chain);
        free(ptq);
        return NULL;
@@ -984,7 +1055,6 @@ static void intel_pt_free_queue(void *priv)
        intel_pt_decoder_free(ptq->decoder);
        zfree(&ptq->event_buf);
        zfree(&ptq->last_branch);
-       zfree(&ptq->last_branch_rb);
        zfree(&ptq->chain);
        free(ptq);
 }
@@ -1152,58 +1222,6 @@ static int intel_pt_setup_queues(struct intel_pt *pt)
        return 0;
 }
 
-static inline void intel_pt_copy_last_branch_rb(struct intel_pt_queue *ptq)
-{
-       struct branch_stack *bs_src = ptq->last_branch_rb;
-       struct branch_stack *bs_dst = ptq->last_branch;
-       size_t nr = 0;
-
-       bs_dst->nr = bs_src->nr;
-
-       if (!bs_src->nr)
-               return;
-
-       nr = ptq->pt->synth_opts.last_branch_sz - ptq->last_branch_pos;
-       memcpy(&bs_dst->entries[0],
-              &bs_src->entries[ptq->last_branch_pos],
-              sizeof(struct branch_entry) * nr);
-
-       if (bs_src->nr >= ptq->pt->synth_opts.last_branch_sz) {
-               memcpy(&bs_dst->entries[nr],
-                      &bs_src->entries[0],
-                      sizeof(struct branch_entry) * ptq->last_branch_pos);
-       }
-}
-
-static inline void intel_pt_reset_last_branch_rb(struct intel_pt_queue *ptq)
-{
-       ptq->last_branch_pos = 0;
-       ptq->last_branch_rb->nr = 0;
-}
-
-static void intel_pt_update_last_branch_rb(struct intel_pt_queue *ptq)
-{
-       const struct intel_pt_state *state = ptq->state;
-       struct branch_stack *bs = ptq->last_branch_rb;
-       struct branch_entry *be;
-
-       if (!ptq->last_branch_pos)
-               ptq->last_branch_pos = ptq->pt->synth_opts.last_branch_sz;
-
-       ptq->last_branch_pos -= 1;
-
-       be              = &bs->entries[ptq->last_branch_pos];
-       be->from        = state->from_ip;
-       be->to          = state->to_ip;
-       be->flags.abort = !!(state->flags & INTEL_PT_ABORT_TX);
-       be->flags.in_tx = !!(state->flags & INTEL_PT_IN_TX);
-       /* No support for mispredict */
-       be->flags.mispred = ptq->pt->mispred_all;
-
-       if (bs->nr < ptq->pt->synth_opts.last_branch_sz)
-               bs->nr += 1;
-}
-
 static inline bool intel_pt_skip_event(struct intel_pt *pt)
 {
        return pt->synth_opts.initial_skip &&
@@ -1271,9 +1289,9 @@ static inline int intel_pt_opt_inject(struct intel_pt *pt,
        return intel_pt_inject_event(event, sample, type);
 }
 
-static int intel_pt_deliver_synth_b_event(struct intel_pt *pt,
-                                         union perf_event *event,
-                                         struct perf_sample *sample, u64 type)
+static int intel_pt_deliver_synth_event(struct intel_pt *pt,
+                                       union perf_event *event,
+                                       struct perf_sample *sample, u64 type)
 {
        int ret;
 
@@ -1333,8 +1351,8 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq)
                ptq->last_br_cyc_cnt = ptq->ipc_cyc_cnt;
        }
 
-       return intel_pt_deliver_synth_b_event(pt, event, &sample,
-                                             pt->branches_sample_type);
+       return intel_pt_deliver_synth_event(pt, event, &sample,
+                                           pt->branches_sample_type);
 }
 
 static void intel_pt_prep_sample(struct intel_pt *pt,
@@ -1352,27 +1370,12 @@ static void intel_pt_prep_sample(struct intel_pt *pt,
        }
 
        if (pt->synth_opts.last_branch) {
-               intel_pt_copy_last_branch_rb(ptq);
+               thread_stack__br_sample(ptq->thread, ptq->cpu, ptq->last_branch,
+                                       pt->br_stack_sz);
                sample->branch_stack = ptq->last_branch;
        }
 }
 
-static inline int intel_pt_deliver_synth_event(struct intel_pt *pt,
-                                              struct intel_pt_queue *ptq,
-                                              union perf_event *event,
-                                              struct perf_sample *sample,
-                                              u64 type)
-{
-       int ret;
-
-       ret = intel_pt_deliver_synth_b_event(pt, event, sample, type);
-
-       if (pt->synth_opts.last_branch)
-               intel_pt_reset_last_branch_rb(ptq);
-
-       return ret;
-}
-
 static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
 {
        struct intel_pt *pt = ptq->pt;
@@ -1397,7 +1400,7 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq)
 
        ptq->last_insn_cnt = ptq->state->tot_insn_cnt;
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->instructions_sample_type);
 }
 
@@ -1415,7 +1418,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq)
        sample.id = ptq->pt->transactions_id;
        sample.stream_id = ptq->pt->transactions_id;
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->transactions_sample_type);
 }
 
@@ -1456,7 +1459,7 @@ static int intel_pt_synth_ptwrite_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->ptwrites_sample_type);
 }
 
@@ -1486,7 +1489,7 @@ static int intel_pt_synth_cbr_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->pwr_events_sample_type);
 }
 
@@ -1511,7 +1514,7 @@ static int intel_pt_synth_mwait_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->pwr_events_sample_type);
 }
 
@@ -1536,7 +1539,7 @@ static int intel_pt_synth_pwre_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->pwr_events_sample_type);
 }
 
@@ -1561,7 +1564,7 @@ static int intel_pt_synth_exstop_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->pwr_events_sample_type);
 }
 
@@ -1586,7 +1589,7 @@ static int intel_pt_synth_pwrx_sample(struct intel_pt_queue *ptq)
        sample.raw_size = perf_synth__raw_size(raw);
        sample.raw_data = perf_synth__raw_data(&raw);
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample,
+       return intel_pt_deliver_synth_event(pt, event, &sample,
                                            pt->pwr_events_sample_type);
 }
 
@@ -1680,15 +1683,14 @@ static u64 intel_pt_lbr_flags(u64 info)
        union {
                struct branch_flags flags;
                u64 result;
-       } u = {
-               .flags = {
-                       .mispred        = !!(info & LBR_INFO_MISPRED),
-                       .predicted      = !(info & LBR_INFO_MISPRED),
-                       .in_tx          = !!(info & LBR_INFO_IN_TX),
-                       .abort          = !!(info & LBR_INFO_ABORT),
-                       .cycles         = info & LBR_INFO_CYCLES,
-               }
-       };
+       } u;
+
+       u.result          = 0;
+       u.flags.mispred   = !!(info & LBR_INFO_MISPRED);
+       u.flags.predicted = !(info & LBR_INFO_MISPRED);
+       u.flags.in_tx     = !!(info & LBR_INFO_IN_TX);
+       u.flags.abort     = !!(info & LBR_INFO_ABORT);
+       u.flags.cycles    = info & LBR_INFO_CYCLES;
 
        return u.result;
 }
@@ -1807,7 +1809,9 @@ static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
                        intel_pt_add_lbrs(&br.br_stack, items);
                        sample.branch_stack = &br.br_stack;
                } else if (pt->synth_opts.last_branch) {
-                       intel_pt_copy_last_branch_rb(ptq);
+                       thread_stack__br_sample(ptq->thread, ptq->cpu,
+                                               ptq->last_branch,
+                                               pt->br_stack_sz);
                        sample.branch_stack = ptq->last_branch;
                } else {
                        br.br_stack.nr = 0;
@@ -1842,7 +1846,7 @@ static int intel_pt_synth_pebs_sample(struct intel_pt_queue *ptq)
                sample.transaction = txn;
        }
 
-       return intel_pt_deliver_synth_event(pt, ptq, event, &sample, sample_type);
+       return intel_pt_deliver_synth_event(pt, event, &sample, sample_type);
 }
 
 static int intel_pt_synth_error(struct intel_pt *pt, int code, int cpu,
@@ -1992,12 +1996,15 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
        if (!(state->type & INTEL_PT_BRANCH))
                return 0;
 
-       if (pt->synth_opts.callchain || pt->synth_opts.thread_stack)
-               thread_stack__event(ptq->thread, ptq->cpu, ptq->flags, state->from_ip,
-                                   state->to_ip, ptq->insn_len,
-                                   state->trace_nr);
-       else
+       if (pt->use_thread_stack) {
+               thread_stack__event(ptq->thread, ptq->cpu, ptq->flags,
+                                   state->from_ip, state->to_ip, ptq->insn_len,
+                                   state->trace_nr, pt->callstack,
+                                   pt->br_stack_sz_plus,
+                                   pt->mispred_all);
+       } else {
                thread_stack__set_trace_nr(ptq->thread, ptq->cpu, state->trace_nr);
+       }
 
        if (pt->sample_branches) {
                err = intel_pt_synth_branch_sample(ptq);
@@ -2005,9 +2012,6 @@ static int intel_pt_sample(struct intel_pt_queue *ptq)
                        return err;
        }
 
-       if (pt->synth_opts.last_branch)
-               intel_pt_update_last_branch_rb(ptq);
-
        if (!ptq->sync_switch)
                return 0;
 
@@ -2484,7 +2488,7 @@ static int intel_pt_process_switch(struct intel_pt *pt,
        if (evsel != pt->switch_evsel)
                return 0;
 
-       tid = perf_evsel__intval(evsel, sample, "next_pid");
+       tid = evsel__intval(evsel, sample, "next_pid");
        cpu = sample->cpu;
 
        intel_pt_log("sched_switch: cpu %d tid %d time %"PRIu64" tsc %#"PRIx64"\n",
@@ -2639,6 +2643,13 @@ static int intel_pt_process_event(struct perf_session *session,
        if (err)
                return err;
 
+       if (event->header.type == PERF_RECORD_SAMPLE) {
+               if (pt->synth_opts.add_callchain && !sample->callchain)
+                       intel_pt_add_callchain(pt, sample);
+               if (pt->synth_opts.add_last_branch && !sample->branch_stack)
+                       intel_pt_add_br_stack(pt, sample);
+       }
+
        if (event->header.type == PERF_RECORD_AUX &&
            (event->aux.flags & PERF_AUX_FLAG_TRUNCATED) &&
            pt->synth_opts.errors) {
@@ -2710,11 +2721,21 @@ static void intel_pt_free(struct perf_session *session)
        session->auxtrace = NULL;
        thread__put(pt->unknown_thread);
        addr_filters__exit(&pt->filts);
+       zfree(&pt->chain);
        zfree(&pt->filter);
        zfree(&pt->time_ranges);
        free(pt);
 }
 
+static bool intel_pt_evsel_is_auxtrace(struct perf_session *session,
+                                      struct evsel *evsel)
+{
+       struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt,
+                                          auxtrace);
+
+       return evsel->core.attr.type == pt->pmu_type;
+}
+
 static int intel_pt_process_auxtrace_event(struct perf_session *session,
                                           union perf_event *event,
                                           struct perf_tool *tool __maybe_unused)
@@ -3016,7 +3037,7 @@ static struct evsel *intel_pt_find_sched_switch(struct evlist *evlist)
        struct evsel *evsel;
 
        evlist__for_each_entry_reverse(evlist, evsel) {
-               const char *name = perf_evsel__name(evsel);
+               const char *name = evsel__name(evsel);
 
                if (!strcmp(name, "sched:sched_switch"))
                        return evsel;
@@ -3310,6 +3331,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        pt->auxtrace.flush_events = intel_pt_flush;
        pt->auxtrace.free_events = intel_pt_free_events;
        pt->auxtrace.free = intel_pt_free;
+       pt->auxtrace.evsel_is_auxtrace = intel_pt_evsel_is_auxtrace;
        session->auxtrace = &pt->auxtrace;
 
        if (dump_trace)
@@ -3338,6 +3360,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
                    !session->itrace_synth_opts->inject) {
                        pt->synth_opts.branches = false;
                        pt->synth_opts.callchain = true;
+                       pt->synth_opts.add_callchain = true;
                }
                pt->synth_opts.thread_stack =
                                session->itrace_synth_opts->thread_stack;
@@ -3370,14 +3393,54 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
                pt->branches_filter |= PERF_IP_FLAG_RETURN |
                                       PERF_IP_FLAG_TRACE_BEGIN;
 
-       if (pt->synth_opts.callchain && !symbol_conf.use_callchain) {
+       if ((pt->synth_opts.callchain || pt->synth_opts.add_callchain) &&
+           !symbol_conf.use_callchain) {
                symbol_conf.use_callchain = true;
                if (callchain_register_param(&callchain_param) < 0) {
                        symbol_conf.use_callchain = false;
                        pt->synth_opts.callchain = false;
+                       pt->synth_opts.add_callchain = false;
                }
        }
 
+       if (pt->synth_opts.add_callchain) {
+               err = intel_pt_callchain_init(pt);
+               if (err)
+                       goto err_delete_thread;
+       }
+
+       if (pt->synth_opts.last_branch || pt->synth_opts.add_last_branch) {
+               pt->br_stack_sz = pt->synth_opts.last_branch_sz;
+               pt->br_stack_sz_plus = pt->br_stack_sz;
+       }
+
+       if (pt->synth_opts.add_last_branch) {
+               err = intel_pt_br_stack_init(pt);
+               if (err)
+                       goto err_delete_thread;
+               /*
+                * Additional branch stack size to cater for tracing from the
+                * actual sample ip to where the sample time is recorded.
+                * Measured at about 200 branches, but generously set to 1024.
+                * If kernel space is not being traced, then add just 1 for the
+                * branch to kernel space.
+                */
+               if (intel_pt_tracing_kernel(pt))
+                       pt->br_stack_sz_plus += 1024;
+               else
+                       pt->br_stack_sz_plus += 1;
+       }
+
+       pt->use_thread_stack = pt->synth_opts.callchain ||
+                              pt->synth_opts.add_callchain ||
+                              pt->synth_opts.thread_stack ||
+                              pt->synth_opts.last_branch ||
+                              pt->synth_opts.add_last_branch;
+
+       pt->callstack = pt->synth_opts.callchain ||
+                       pt->synth_opts.add_callchain ||
+                       pt->synth_opts.thread_stack;
+
        err = intel_pt_synth_events(pt, session);
        if (err)
                goto err_delete_thread;
@@ -3400,6 +3463,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
        return 0;
 
 err_delete_thread:
+       zfree(&pt->chain);
        thread__zput(pt->unknown_thread);
 err_free_queues:
        intel_pt_log_disable();
index 97142e9671be3ed5b2d494a4194531b444510567..8ed2135893bb15fb743a67337d59531a40d0478c 100644 (file)
@@ -736,6 +736,12 @@ int machine__process_switch_event(struct machine *machine __maybe_unused,
        return 0;
 }
 
+static int is_bpf_image(const char *name)
+{
+       return strncmp(name, "bpf_trampoline_", sizeof("bpf_trampoline_") - 1) ||
+              strncmp(name, "bpf_dispatcher_", sizeof("bpf_dispatcher_") - 1);
+}
+
 static int machine__process_ksymbol_register(struct machine *machine,
                                             union perf_event *event,
                                             struct perf_sample *sample __maybe_unused)
@@ -759,6 +765,12 @@ static int machine__process_ksymbol_register(struct machine *machine,
                map->start = event->ksymbol.addr;
                map->end = map->start + event->ksymbol.len;
                maps__insert(&machine->kmaps, map);
+               dso__set_loaded(dso);
+
+               if (is_bpf_image(event->ksymbol.name)) {
+                       dso->binary_type = DSO_BINARY_TYPE__BPF_IMAGE;
+                       dso__set_long_name(dso, "", false);
+               }
        }
 
        sym = symbol__new(map->map_ip(map, map->start),
@@ -2178,6 +2190,303 @@ static int remove_loops(struct branch_entry *l, int nr,
        return nr;
 }
 
+static int lbr_callchain_add_kernel_ip(struct thread *thread,
+                                      struct callchain_cursor *cursor,
+                                      struct perf_sample *sample,
+                                      struct symbol **parent,
+                                      struct addr_location *root_al,
+                                      u64 branch_from,
+                                      bool callee, int end)
+{
+       struct ip_callchain *chain = sample->callchain;
+       u8 cpumode = PERF_RECORD_MISC_USER;
+       int err, i;
+
+       if (callee) {
+               for (i = 0; i < end + 1; i++) {
+                       err = add_callchain_ip(thread, cursor, parent,
+                                              root_al, &cpumode, chain->ips[i],
+                                              false, NULL, NULL, branch_from);
+                       if (err)
+                               return err;
+               }
+               return 0;
+       }
+
+       for (i = end; i >= 0; i--) {
+               err = add_callchain_ip(thread, cursor, parent,
+                                      root_al, &cpumode, chain->ips[i],
+                                      false, NULL, NULL, branch_from);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void save_lbr_cursor_node(struct thread *thread,
+                                struct callchain_cursor *cursor,
+                                int idx)
+{
+       struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+
+       if (!lbr_stitch)
+               return;
+
+       if (cursor->pos == cursor->nr) {
+               lbr_stitch->prev_lbr_cursor[idx].valid = false;
+               return;
+       }
+
+       if (!cursor->curr)
+               cursor->curr = cursor->first;
+       else
+               cursor->curr = cursor->curr->next;
+       memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr,
+              sizeof(struct callchain_cursor_node));
+
+       lbr_stitch->prev_lbr_cursor[idx].valid = true;
+       cursor->pos++;
+}
+
+static int lbr_callchain_add_lbr_ip(struct thread *thread,
+                                   struct callchain_cursor *cursor,
+                                   struct perf_sample *sample,
+                                   struct symbol **parent,
+                                   struct addr_location *root_al,
+                                   u64 *branch_from,
+                                   bool callee)
+{
+       struct branch_stack *lbr_stack = sample->branch_stack;
+       struct branch_entry *entries = perf_sample__branch_entries(sample);
+       u8 cpumode = PERF_RECORD_MISC_USER;
+       int lbr_nr = lbr_stack->nr;
+       struct branch_flags *flags;
+       int err, i;
+       u64 ip;
+
+       /*
+        * The curr and pos are not used in writing session. They are cleared
+        * in callchain_cursor_commit() when the writing session is closed.
+        * Using curr and pos to track the current cursor node.
+        */
+       if (thread->lbr_stitch) {
+               cursor->curr = NULL;
+               cursor->pos = cursor->nr;
+               if (cursor->nr) {
+                       cursor->curr = cursor->first;
+                       for (i = 0; i < (int)(cursor->nr - 1); i++)
+                               cursor->curr = cursor->curr->next;
+               }
+       }
+
+       if (callee) {
+               /* Add LBR ip from first entries.to */
+               ip = entries[0].to;
+               flags = &entries[0].flags;
+               *branch_from = entries[0].from;
+               err = add_callchain_ip(thread, cursor, parent,
+                                      root_al, &cpumode, ip,
+                                      true, flags, NULL,
+                                      *branch_from);
+               if (err)
+                       return err;
+
+               /*
+                * The number of cursor node increases.
+                * Move the current cursor node.
+                * But does not need to save current cursor node for entry 0.
+                * It's impossible to stitch the whole LBRs of previous sample.
+                */
+               if (thread->lbr_stitch && (cursor->pos != cursor->nr)) {
+                       if (!cursor->curr)
+                               cursor->curr = cursor->first;
+                       else
+                               cursor->curr = cursor->curr->next;
+                       cursor->pos++;
+               }
+
+               /* Add LBR ip from entries.from one by one. */
+               for (i = 0; i < lbr_nr; i++) {
+                       ip = entries[i].from;
+                       flags = &entries[i].flags;
+                       err = add_callchain_ip(thread, cursor, parent,
+                                              root_al, &cpumode, ip,
+                                              true, flags, NULL,
+                                              *branch_from);
+                       if (err)
+                               return err;
+                       save_lbr_cursor_node(thread, cursor, i);
+               }
+               return 0;
+       }
+
+       /* Add LBR ip from entries.from one by one. */
+       for (i = lbr_nr - 1; i >= 0; i--) {
+               ip = entries[i].from;
+               flags = &entries[i].flags;
+               err = add_callchain_ip(thread, cursor, parent,
+                                      root_al, &cpumode, ip,
+                                      true, flags, NULL,
+                                      *branch_from);
+               if (err)
+                       return err;
+               save_lbr_cursor_node(thread, cursor, i);
+       }
+
+       /* Add LBR ip from first entries.to */
+       ip = entries[0].to;
+       flags = &entries[0].flags;
+       *branch_from = entries[0].from;
+       err = add_callchain_ip(thread, cursor, parent,
+                              root_al, &cpumode, ip,
+                              true, flags, NULL,
+                              *branch_from);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int lbr_callchain_add_stitched_lbr_ip(struct thread *thread,
+                                            struct callchain_cursor *cursor)
+{
+       struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+       struct callchain_cursor_node *cnode;
+       struct stitch_list *stitch_node;
+       int err;
+
+       list_for_each_entry(stitch_node, &lbr_stitch->lists, node) {
+               cnode = &stitch_node->cursor;
+
+               err = callchain_cursor_append(cursor, cnode->ip,
+                                             &cnode->ms,
+                                             cnode->branch,
+                                             &cnode->branch_flags,
+                                             cnode->nr_loop_iter,
+                                             cnode->iter_cycles,
+                                             cnode->branch_from,
+                                             cnode->srcline);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static struct stitch_list *get_stitch_node(struct thread *thread)
+{
+       struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+       struct stitch_list *stitch_node;
+
+       if (!list_empty(&lbr_stitch->free_lists)) {
+               stitch_node = list_first_entry(&lbr_stitch->free_lists,
+                                              struct stitch_list, node);
+               list_del(&stitch_node->node);
+
+               return stitch_node;
+       }
+
+       return malloc(sizeof(struct stitch_list));
+}
+
+static bool has_stitched_lbr(struct thread *thread,
+                            struct perf_sample *cur,
+                            struct perf_sample *prev,
+                            unsigned int max_lbr,
+                            bool callee)
+{
+       struct branch_stack *cur_stack = cur->branch_stack;
+       struct branch_entry *cur_entries = perf_sample__branch_entries(cur);
+       struct branch_stack *prev_stack = prev->branch_stack;
+       struct branch_entry *prev_entries = perf_sample__branch_entries(prev);
+       struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+       int i, j, nr_identical_branches = 0;
+       struct stitch_list *stitch_node;
+       u64 cur_base, distance;
+
+       if (!cur_stack || !prev_stack)
+               return false;
+
+       /* Find the physical index of the base-of-stack for current sample. */
+       cur_base = max_lbr - cur_stack->nr + cur_stack->hw_idx + 1;
+
+       distance = (prev_stack->hw_idx > cur_base) ? (prev_stack->hw_idx - cur_base) :
+                                                    (max_lbr + prev_stack->hw_idx - cur_base);
+       /* Previous sample has shorter stack. Nothing can be stitched. */
+       if (distance + 1 > prev_stack->nr)
+               return false;
+
+       /*
+        * Check if there are identical LBRs between two samples.
+        * Identicall LBRs must have same from, to and flags values. Also,
+        * they have to be saved in the same LBR registers (same physical
+        * index).
+        *
+        * Starts from the base-of-stack of current sample.
+        */
+       for (i = distance, j = cur_stack->nr - 1; (i >= 0) && (j >= 0); i--, j--) {
+               if ((prev_entries[i].from != cur_entries[j].from) ||
+                   (prev_entries[i].to != cur_entries[j].to) ||
+                   (prev_entries[i].flags.value != cur_entries[j].flags.value))
+                       break;
+               nr_identical_branches++;
+       }
+
+       if (!nr_identical_branches)
+               return false;
+
+       /*
+        * Save the LBRs between the base-of-stack of previous sample
+        * and the base-of-stack of current sample into lbr_stitch->lists.
+        * These LBRs will be stitched later.
+        */
+       for (i = prev_stack->nr - 1; i > (int)distance; i--) {
+
+               if (!lbr_stitch->prev_lbr_cursor[i].valid)
+                       continue;
+
+               stitch_node = get_stitch_node(thread);
+               if (!stitch_node)
+                       return false;
+
+               memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i],
+                      sizeof(struct callchain_cursor_node));
+
+               if (callee)
+                       list_add(&stitch_node->node, &lbr_stitch->lists);
+               else
+                       list_add_tail(&stitch_node->node, &lbr_stitch->lists);
+       }
+
+       return true;
+}
+
+static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr)
+{
+       if (thread->lbr_stitch)
+               return true;
+
+       thread->lbr_stitch = zalloc(sizeof(*thread->lbr_stitch));
+       if (!thread->lbr_stitch)
+               goto err;
+
+       thread->lbr_stitch->prev_lbr_cursor = calloc(max_lbr + 1, sizeof(struct callchain_cursor_node));
+       if (!thread->lbr_stitch->prev_lbr_cursor)
+               goto free_lbr_stitch;
+
+       INIT_LIST_HEAD(&thread->lbr_stitch->lists);
+       INIT_LIST_HEAD(&thread->lbr_stitch->free_lists);
+
+       return true;
+
+free_lbr_stitch:
+       zfree(&thread->lbr_stitch);
+err:
+       pr_warning("Failed to allocate space for stitched LBRs. Disable LBR stitch\n");
+       thread->lbr_stitch_enable = false;
+       return false;
+}
+
 /*
  * Recolve LBR callstack chain sample
  * Return:
@@ -2190,12 +2499,16 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
                                        struct perf_sample *sample,
                                        struct symbol **parent,
                                        struct addr_location *root_al,
-                                       int max_stack)
+                                       int max_stack,
+                                       unsigned int max_lbr)
 {
+       bool callee = (callchain_param.order == ORDER_CALLEE);
        struct ip_callchain *chain = sample->callchain;
        int chain_nr = min(max_stack, (int)chain->nr), i;
-       u8 cpumode = PERF_RECORD_MISC_USER;
-       u64 ip, branch_from = 0;
+       struct lbr_stitch *lbr_stitch;
+       bool stitched_lbr = false;
+       u64 branch_from = 0;
+       int err;
 
        for (i = 0; i < chain_nr; i++) {
                if (chain->ips[i] == PERF_CONTEXT_USER)
@@ -2203,71 +2516,65 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
        }
 
        /* LBR only affects the user callchain */
-       if (i != chain_nr) {
-               struct branch_stack *lbr_stack = sample->branch_stack;
-               struct branch_entry *entries = perf_sample__branch_entries(sample);
-               int lbr_nr = lbr_stack->nr, j, k;
-               bool branch;
-               struct branch_flags *flags;
-               /*
-                * LBR callstack can only get user call chain.
-                * The mix_chain_nr is kernel call chain
-                * number plus LBR user call chain number.
-                * i is kernel call chain number,
-                * 1 is PERF_CONTEXT_USER,
-                * lbr_nr + 1 is the user call chain number.
-                * For details, please refer to the comments
-                * in callchain__printf
-                */
-               int mix_chain_nr = i + 1 + lbr_nr + 1;
+       if (i == chain_nr)
+               return 0;
 
-               for (j = 0; j < mix_chain_nr; j++) {
-                       int err;
-                       branch = false;
-                       flags = NULL;
+       if (thread->lbr_stitch_enable && !sample->no_hw_idx &&
+           (max_lbr > 0) && alloc_lbr_stitch(thread, max_lbr)) {
+               lbr_stitch = thread->lbr_stitch;
 
-                       if (callchain_param.order == ORDER_CALLEE) {
-                               if (j < i + 1)
-                                       ip = chain->ips[j];
-                               else if (j > i + 1) {
-                                       k = j - i - 2;
-                                       ip = entries[k].from;
-                                       branch = true;
-                                       flags = &entries[k].flags;
-                               } else {
-                                       ip = entries[0].to;
-                                       branch = true;
-                                       flags = &entries[0].flags;
-                                       branch_from = entries[0].from;
-                               }
-                       } else {
-                               if (j < lbr_nr) {
-                                       k = lbr_nr - j - 1;
-                                       ip = entries[k].from;
-                                       branch = true;
-                                       flags = &entries[k].flags;
-                               }
-                               else if (j > lbr_nr)
-                                       ip = chain->ips[i + 1 - (j - lbr_nr)];
-                               else {
-                                       ip = entries[0].to;
-                                       branch = true;
-                                       flags = &entries[0].flags;
-                                       branch_from = entries[0].from;
-                               }
-                       }
+               stitched_lbr = has_stitched_lbr(thread, sample,
+                                               &lbr_stitch->prev_sample,
+                                               max_lbr, callee);
 
-                       err = add_callchain_ip(thread, cursor, parent,
-                                              root_al, &cpumode, ip,
-                                              branch, flags, NULL,
-                                              branch_from);
+               if (!stitched_lbr && !list_empty(&lbr_stitch->lists)) {
+                       list_replace_init(&lbr_stitch->lists,
+                                         &lbr_stitch->free_lists);
+               }
+               memcpy(&lbr_stitch->prev_sample, sample, sizeof(*sample));
+       }
+
+       if (callee) {
+               /* Add kernel ip */
+               err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
+                                                 parent, root_al, branch_from,
+                                                 true, i);
+               if (err)
+                       goto error;
+
+               err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent,
+                                              root_al, &branch_from, true);
+               if (err)
+                       goto error;
+
+               if (stitched_lbr) {
+                       err = lbr_callchain_add_stitched_lbr_ip(thread, cursor);
                        if (err)
-                               return (err < 0) ? err : 0;
+                               goto error;
                }
-               return 1;
+
+       } else {
+               if (stitched_lbr) {
+                       err = lbr_callchain_add_stitched_lbr_ip(thread, cursor);
+                       if (err)
+                               goto error;
+               }
+               err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent,
+                                              root_al, &branch_from, false);
+               if (err)
+                       goto error;
+
+               /* Add kernel ip */
+               err = lbr_callchain_add_kernel_ip(thread, cursor, sample,
+                                                 parent, root_al, branch_from,
+                                                 false, i);
+               if (err)
+                       goto error;
        }
+       return 1;
 
-       return 0;
+error:
+       return (err < 0) ? err : 0;
 }
 
 static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
@@ -2311,9 +2618,12 @@ static int thread__resolve_callchain_sample(struct thread *thread,
        if (chain)
                chain_nr = chain->nr;
 
-       if (perf_evsel__has_branch_callstack(evsel)) {
+       if (evsel__has_branch_callstack(evsel)) {
+               struct perf_env *env = evsel__env(evsel);
+
                err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
-                                                  root_al, max_stack);
+                                                  root_al, max_stack,
+                                                  !env ? 0 : env->max_branches);
                if (err)
                        return (err < 0) ? err : 0;
        }
index 797d86a1ab0957406268cb53e34cfd231e00486d..c84f5841c7abd3bed305a06aea0274a91dc61ddc 100644 (file)
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <inttypes.h>
+#include <asm/bug.h>
 #include <linux/bitmap.h>
 #include <linux/kernel.h>
 #include <linux/zalloc.h>
@@ -95,7 +96,7 @@ int mem2node__init(struct mem2node *map, struct perf_env *env)
 
        /* Cut unused entries, due to merging. */
        tmp_entries = realloc(entries, sizeof(*entries) * j);
-       if (tmp_entries)
+       if (tmp_entries || WARN_ON_ONCE(j == 0))
                entries = tmp_entries;
 
        for (i = 0; i < j; i++) {
index 926449a7cdbfff742c623200c307239fff41bba2..b071df373f8b923e5188033f7a1d04a04b50d930 100644 (file)
@@ -90,6 +90,7 @@ struct egroup {
        const char *metric_name;
        const char *metric_expr;
        const char *metric_unit;
+       int runtime;
 };
 
 static struct evsel *find_evsel_group(struct evlist *perf_evlist,
@@ -202,6 +203,7 @@ static int metricgroup__setup_events(struct list_head *groups,
                expr->metric_name = eg->metric_name;
                expr->metric_unit = eg->metric_unit;
                expr->metric_events = metric_events;
+               expr->runtime = eg->runtime;
                list_add(&expr->nd, &me->head);
        }
 
@@ -485,6 +487,45 @@ static bool metricgroup__has_constraint(struct pmu_event *pe)
        return false;
 }
 
+int __weak arch_get_runtimeparam(void)
+{
+       return 1;
+}
+
+static int __metricgroup__add_metric(struct strbuf *events,
+               struct list_head *group_list, struct pmu_event *pe, int runtime)
+{
+
+       const char **ids;
+       int idnum;
+       struct egroup *eg;
+
+       if (expr__find_other(pe->metric_expr, NULL, &ids, &idnum, runtime) < 0)
+               return -EINVAL;
+
+       if (events->len > 0)
+               strbuf_addf(events, ",");
+
+       if (metricgroup__has_constraint(pe))
+               metricgroup__add_metric_non_group(events, ids, idnum);
+       else
+               metricgroup__add_metric_weak_group(events, ids, idnum);
+
+       eg = malloc(sizeof(*eg));
+       if (!eg)
+               return -ENOMEM;
+
+       eg->ids = ids;
+       eg->idnum = idnum;
+       eg->metric_name = pe->metric_name;
+       eg->metric_expr = pe->metric_expr;
+       eg->metric_unit = pe->unit;
+       eg->runtime = runtime;
+       list_add_tail(&eg->nd, group_list);
+
+       return 0;
+}
+
 static int metricgroup__add_metric(const char *metric, struct strbuf *events,
                                   struct list_head *group_list)
 {
@@ -504,35 +545,26 @@ static int metricgroup__add_metric(const char *metric, struct strbuf *events,
                        continue;
                if (match_metric(pe->metric_group, metric) ||
                    match_metric(pe->metric_name, metric)) {
-                       const char **ids;
-                       int idnum;
-                       struct egroup *eg;
 
                        pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name);
 
-                       if (expr__find_other(pe->metric_expr,
-                                            NULL, &ids, &idnum) < 0)
-                               continue;
-                       if (events->len > 0)
-                               strbuf_addf(events, ",");
+                       if (!strstr(pe->metric_expr, "?")) {
+                               ret = __metricgroup__add_metric(events, group_list, pe, 1);
+                       } else {
+                               int j, count;
 
-                       if (metricgroup__has_constraint(pe))
-                               metricgroup__add_metric_non_group(events, ids, idnum);
-                       else
-                               metricgroup__add_metric_weak_group(events, ids, idnum);
+                               count = arch_get_runtimeparam();
 
-                       eg = malloc(sizeof(struct egroup));
-                       if (!eg) {
-                               ret = -ENOMEM;
-                               break;
+                               /* This loop is added to create multiple
+                                * events depend on count value and add
+                                * those events to group_list.
+                                */
+
+                               for (j = 0; j < count; j++)
+                                       ret = __metricgroup__add_metric(events, group_list, pe, j);
                        }
-                       eg->ids = ids;
-                       eg->idnum = idnum;
-                       eg->metric_name = pe->metric_name;
-                       eg->metric_expr = pe->metric_expr;
-                       eg->metric_unit = pe->unit;
-                       list_add_tail(&eg->nd, group_list);
-                       ret = 0;
+                       if (ret == -ENOMEM)
+                               break;
                }
        }
        return ret;
index 475c7f912864f9ff512cf9d189bed9ed1b4fa7a3..6b09eb30b4ecf8bf0434e3520a4904f14f8da5f7 100644 (file)
@@ -22,6 +22,7 @@ struct metric_expr {
        const char *metric_name;
        const char *metric_unit;
        struct evsel **metric_events;
+       int runtime;
 };
 
 struct metric_event *metricgroup__lookup(struct rblist *metric_events,
@@ -34,4 +35,5 @@ int metricgroup__parse_groups(const struct option *opt,
 void metricgroup__print(bool metrics, bool groups, char *filter,
                        bool raw, bool details);
 bool metricgroup__has_metric(const char *metric);
+int arch_get_runtimeparam(void);
 #endif
index 359db2b1fcef3096bdcb36316c1374454876de13..48c8f609441b4c16b85f14645a8fef917d1a9ca3 100644 (file)
@@ -314,7 +314,7 @@ static int __ordered_events__flush(struct ordered_events *oe, enum oe_flush how,
        case OE_FLUSH__NONE:
        default:
                break;
-       };
+       }
 
        pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE  %s, nr_events %u\n",
                   str[how], oe->nr_events);
index 10107747b36176f2b49ee7aa65f2607de5645fe6..b7a0518d607d8970ce4f4fcc3e21b020a9aaafca 100644 (file)
@@ -461,7 +461,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,
                        cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
                                                 PERF_COUNT_HW_CACHE_OP_MAX);
                        if (cache_op >= 0) {
-                               if (!perf_evsel__is_cache_op_valid(cache_type, cache_op))
+                               if (!evsel__is_cache_op_valid(cache_type, cache_op))
                                        return -EINVAL;
                                continue;
                        }
@@ -1482,6 +1482,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
 
                list_for_each_entry_safe(pos, tmp, &config_terms, list) {
                        list_del_init(&pos->list);
+                       zfree(&pos->val.str);
                        free(pos);
                }
                return -EINVAL;
@@ -1870,7 +1871,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
                evsel->precise_max         = mod.precise_max;
                evsel->weak_group          = mod.weak;
 
-               if (perf_evsel__is_group_leader(evsel))
+               if (evsel__is_group_leader(evsel))
                        evsel->core.attr.pinned = mod.pinned;
        }
 
@@ -2190,6 +2191,29 @@ int parse_events_option(const struct option *opt, const char *str,
        return ret;
 }
 
+int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset)
+{
+       struct evlist **evlistp = opt->value;
+       int ret;
+
+       if (*evlistp == NULL) {
+               *evlistp = evlist__new();
+
+               if (*evlistp == NULL) {
+                       fprintf(stderr, "Not enough memory to create evlist\n");
+                       return -1;
+               }
+       }
+
+       ret = parse_events_option(opt, str, unset);
+       if (ret) {
+               evlist__delete(*evlistp);
+               *evlistp = NULL;
+       }
+
+       return ret;
+}
+
 static int
 foreach_evsel_in_last_glob(struct evlist *evlist,
                           int (*func)(struct evsel *evsel,
@@ -2237,7 +2261,7 @@ static int set_filter(struct evsel *evsel, const void *arg)
        }
 
        if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
-               if (perf_evsel__append_tp_filter(evsel, str) < 0) {
+               if (evsel__append_tp_filter(evsel, str) < 0) {
                        fprintf(stderr,
                                "not enough memory to hold filter string\n");
                        return -1;
@@ -2262,7 +2286,7 @@ static int set_filter(struct evsel *evsel, const void *arg)
                return -1;
        }
 
-       if (perf_evsel__append_addr_filter(evsel, str) < 0) {
+       if (evsel__append_addr_filter(evsel, str) < 0) {
                fprintf(stderr,
                        "not enough memory to hold filter string\n");
                return -1;
@@ -2293,7 +2317,7 @@ static int add_exclude_perf_filter(struct evsel *evsel,
 
        snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid());
 
-       if (perf_evsel__append_tp_filter(evsel, new_filter) < 0) {
+       if (evsel__append_tp_filter(evsel, new_filter) < 0) {
                fprintf(stderr,
                        "not enough memory to hold filter string\n");
                return -1;
@@ -2603,12 +2627,11 @@ restart:
        for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
                for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
                        /* skip invalid cache type */
-                       if (!perf_evsel__is_cache_op_valid(type, op))
+                       if (!evsel__is_cache_op_valid(type, op))
                                continue;
 
                        for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
-                               __perf_evsel__hw_cache_type_op_res_name(type, op, i,
-                                                                       name, sizeof(name));
+                               __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name));
                                if (event_glob != NULL && !strglobmatch(name, event_glob))
                                        continue;
 
index 27596cbd0ba06a73af36557d818dbeb71ef91ed0..6ead9661238c398807ddee5e0b2412c8284731be 100644 (file)
@@ -31,6 +31,7 @@ bool have_tracepoints(struct list_head *evlist);
 const char *event_type(int type);
 
 int parse_events_option(const struct option *opt, const char *str, int unset);
+int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset);
 int parse_events(struct evlist *evlist, const char *str,
                 struct parse_events_error *error);
 int parse_events_terms(struct list_head *terms, const char *str);
index baa48f28d57d56c7dd8f8374583440842b61c65f..c589fc42f0586a214a80f5ba6abc86c02cf5b4b9 100644 (file)
@@ -286,6 +286,7 @@ no-overwrite                { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
 percore                        { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
 aux-output             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
 aux-sample-size                { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
+r{num_raw_hex}         { return raw(yyscanner); }
 ,                      { return ','; }
 "/"                    { BEGIN(INITIAL); return '/'; }
 {name_minus}           { return str(yyscanner, PE_NAME); }
index 94f8bcd835826de88b83c8df50141bafdffce7f2..c4ca932d092d8d29f588e6aa33c5990f786c7217 100644 (file)
@@ -44,7 +44,7 @@ static void free_list_evsel(struct list_head* list_evsel)
 
        list_for_each_entry_safe(evsel, tmp, list_evsel, core.node) {
                list_del_init(&evsel->core.node);
-               perf_evsel__delete(evsel);
+               evsel__delete(evsel);
        }
        free(list_evsel);
 }
@@ -326,6 +326,7 @@ PE_NAME opt_pmu_config
        }
        parse_events_terms__delete($2);
        parse_events_terms__delete(orig_terms);
+       free(pattern);
        free($1);
        $$ = list;
 #undef CLEANUP_YYABORT
@@ -706,6 +707,15 @@ event_term
 }
 
 event_term:
+PE_RAW
+{
+       struct parse_events_term *term;
+
+       ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_CONFIG,
+                                       NULL, $1, false, &@1, NULL));
+       $$ = term;
+}
+|
 PE_NAME '=' PE_NAME
 {
        struct parse_events_term *term;
diff --git a/tools/perf/util/perf_api_probe.c b/tools/perf/util/perf_api_probe.c
new file mode 100644 (file)
index 0000000..1337965
--- /dev/null
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "perf-sys.h"
+#include "util/cloexec.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
+#include "util/parse-events.h"
+#include "util/perf_api_probe.h"
+#include <perf/cpumap.h>
+#include <errno.h>
+
+typedef void (*setup_probe_fn_t)(struct evsel *evsel);
+
+static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
+{
+       struct evlist *evlist;
+       struct evsel *evsel;
+       unsigned long flags = perf_event_open_cloexec_flag();
+       int err = -EAGAIN, fd;
+       static pid_t pid = -1;
+
+       evlist = evlist__new();
+       if (!evlist)
+               return -ENOMEM;
+
+       if (parse_events(evlist, str, NULL))
+               goto out_delete;
+
+       evsel = evlist__first(evlist);
+
+       while (1) {
+               fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
+               if (fd < 0) {
+                       if (pid == -1 && errno == EACCES) {
+                               pid = 0;
+                               continue;
+                       }
+                       goto out_delete;
+               }
+               break;
+       }
+       close(fd);
+
+       fn(evsel);
+
+       fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
+       if (fd < 0) {
+               if (errno == EINVAL)
+                       err = -EINVAL;
+               goto out_delete;
+       }
+       close(fd);
+       err = 0;
+
+out_delete:
+       evlist__delete(evlist);
+       return err;
+}
+
+static bool perf_probe_api(setup_probe_fn_t fn)
+{
+       const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
+       struct perf_cpu_map *cpus;
+       int cpu, ret, i = 0;
+
+       cpus = perf_cpu_map__new(NULL);
+       if (!cpus)
+               return false;
+       cpu = cpus->map[0];
+       perf_cpu_map__put(cpus);
+
+       do {
+               ret = perf_do_probe_api(fn, cpu, try[i++]);
+               if (!ret)
+                       return true;
+       } while (ret == -EAGAIN && try[i]);
+
+       return false;
+}
+
+static void perf_probe_sample_identifier(struct evsel *evsel)
+{
+       evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
+}
+
+static void perf_probe_comm_exec(struct evsel *evsel)
+{
+       evsel->core.attr.comm_exec = 1;
+}
+
+static void perf_probe_context_switch(struct evsel *evsel)
+{
+       evsel->core.attr.context_switch = 1;
+}
+
+bool perf_can_sample_identifier(void)
+{
+       return perf_probe_api(perf_probe_sample_identifier);
+}
+
+bool perf_can_comm_exec(void)
+{
+       return perf_probe_api(perf_probe_comm_exec);
+}
+
+bool perf_can_record_switch_events(void)
+{
+       return perf_probe_api(perf_probe_context_switch);
+}
+
+bool perf_can_record_cpu_wide(void)
+{
+       struct perf_event_attr attr = {
+               .type = PERF_TYPE_SOFTWARE,
+               .config = PERF_COUNT_SW_CPU_CLOCK,
+               .exclude_kernel = 1,
+       };
+       struct perf_cpu_map *cpus;
+       int cpu, fd;
+
+       cpus = perf_cpu_map__new(NULL);
+       if (!cpus)
+               return false;
+       cpu = cpus->map[0];
+       perf_cpu_map__put(cpus);
+
+       fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
+       if (fd < 0)
+               return false;
+       close(fd);
+
+       return true;
+}
+
+/*
+ * Architectures are expected to know if AUX area sampling is supported by the
+ * hardware. Here we check for kernel support.
+ */
+bool perf_can_aux_sample(void)
+{
+       struct perf_event_attr attr = {
+               .size = sizeof(struct perf_event_attr),
+               .exclude_kernel = 1,
+               /*
+                * Non-zero value causes the kernel to calculate the effective
+                * attribute size up to that byte.
+                */
+               .aux_sample_size = 1,
+       };
+       int fd;
+
+       fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
+       /*
+        * If the kernel attribute is big enough to contain aux_sample_size
+        * then we assume that it is supported. We are relying on the kernel to
+        * validate the attribute size before anything else that could be wrong.
+        */
+       if (fd < 0 && errno == E2BIG)
+               return false;
+       if (fd >= 0)
+               close(fd);
+
+       return true;
+}
diff --git a/tools/perf/util/perf_api_probe.h b/tools/perf/util/perf_api_probe.h
new file mode 100644 (file)
index 0000000..706c3c6
--- /dev/null
@@ -0,0 +1,14 @@
+
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_API_PROBE_H
+#define __PERF_API_PROBE_H
+
+#include <stdbool.h>
+
+bool perf_can_aux_sample(void);
+bool perf_can_comm_exec(void);
+bool perf_can_record_cpu_wide(void);
+bool perf_can_record_switch_events(void);
+bool perf_can_sample_identifier(void);
+
+#endif // __PERF_API_PROBE_H
index ef6a63f3d386b5c2f11cde018ef85190c5863808..92bd7fafcce66bbc0d7b7134e6c0937d14765577 100644 (file)
@@ -18,6 +18,7 @@
 #include <regex.h>
 #include <perf/cpumap.h>
 #include "debug.h"
+#include "evsel.h"
 #include "pmu.h"
 #include "parse-events.h"
 #include "header.h"
@@ -849,6 +850,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
 
        INIT_LIST_HEAD(&pmu->format);
        INIT_LIST_HEAD(&pmu->aliases);
+       INIT_LIST_HEAD(&pmu->caps);
        list_splice(&format, &pmu->format);
        list_splice(&aliases, &pmu->aliases);
        list_add_tail(&pmu->list, &pmus);
@@ -869,6 +871,17 @@ static struct perf_pmu *pmu_find(const char *name)
        return NULL;
 }
 
+struct perf_pmu *perf_pmu__find_by_type(unsigned int type)
+{
+       struct perf_pmu *pmu;
+
+       list_for_each_entry(pmu, &pmus, list)
+               if (pmu->type == type)
+                       return pmu;
+
+       return NULL;
+}
+
 struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
 {
        /*
@@ -884,6 +897,25 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
        return NULL;
 }
 
+struct perf_pmu *evsel__find_pmu(struct evsel *evsel)
+{
+       struct perf_pmu *pmu = NULL;
+
+       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+               if (pmu->type == evsel->core.attr.type)
+                       break;
+       }
+
+       return pmu;
+}
+
+bool evsel__is_aux_event(struct evsel *evsel)
+{
+       struct perf_pmu *pmu = evsel__find_pmu(evsel);
+
+       return pmu && pmu->auxtrace;
+}
+
 struct perf_pmu *perf_pmu__find(const char *name)
 {
        struct perf_pmu *pmu;
@@ -1574,3 +1606,84 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
        va_end(args);
        return ret;
 }
+
+static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
+{
+       struct perf_pmu_caps *caps = zalloc(sizeof(*caps));
+
+       if (!caps)
+               return -ENOMEM;
+
+       caps->name = strdup(name);
+       if (!caps->name)
+               goto free_caps;
+       caps->value = strndup(value, strlen(value) - 1);
+       if (!caps->value)
+               goto free_name;
+       list_add_tail(&caps->list, list);
+       return 0;
+
+free_name:
+       zfree(caps->name);
+free_caps:
+       free(caps);
+
+       return -ENOMEM;
+}
+
+/*
+ * Reading/parsing the given pmu capabilities, which should be located at:
+ * /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes.
+ * Return the number of capabilities
+ */
+int perf_pmu__caps_parse(struct perf_pmu *pmu)
+{
+       struct stat st;
+       char caps_path[PATH_MAX];
+       const char *sysfs = sysfs__mountpoint();
+       DIR *caps_dir;
+       struct dirent *evt_ent;
+       int nr_caps = 0;
+
+       if (!sysfs)
+               return -1;
+
+       snprintf(caps_path, PATH_MAX,
+                "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name);
+
+       if (stat(caps_path, &st) < 0)
+               return 0;       /* no error if caps does not exist */
+
+       caps_dir = opendir(caps_path);
+       if (!caps_dir)
+               return -EINVAL;
+
+       while ((evt_ent = readdir(caps_dir)) != NULL) {
+               char path[PATH_MAX + NAME_MAX + 1];
+               char *name = evt_ent->d_name;
+               char value[128];
+               FILE *file;
+
+               if (!strcmp(name, ".") || !strcmp(name, ".."))
+                       continue;
+
+               snprintf(path, sizeof(path), "%s/%s", caps_path, name);
+
+               file = fopen(path, "r");
+               if (!file)
+                       continue;
+
+               if (!fgets(value, sizeof(value), file) ||
+                   (perf_pmu__new_caps(&pmu->caps, name, value) < 0)) {
+                       fclose(file);
+                       continue;
+               }
+
+               nr_caps++;
+               fclose(file);
+       }
+
+       closedir(caps_dir);
+
+       return nr_caps;
+}
index 5fb3f16828dfbfb10bfeb3dbb4c7fa0ceebefe35..cb6fbec50313326af5c718b8e42701d842b3bbf6 100644 (file)
@@ -22,6 +22,12 @@ enum {
 
 struct perf_event_attr;
 
+struct perf_pmu_caps {
+       char *name;
+       char *value;
+       struct list_head list;
+};
+
 struct perf_pmu {
        char *name;
        __u32 type;
@@ -33,6 +39,7 @@ struct perf_pmu {
        struct perf_cpu_map *cpus;
        struct list_head format;  /* HEAD struct perf_pmu_format -> list */
        struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
+       struct list_head caps;    /* HEAD struct perf_pmu_caps -> list */
        struct list_head list;    /* ELEM */
 };
 
@@ -65,6 +72,7 @@ struct perf_pmu_alias {
 };
 
 struct perf_pmu *perf_pmu__find(const char *name);
+struct perf_pmu *perf_pmu__find_by_type(unsigned int type);
 int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
                     struct list_head *head_terms,
                     struct parse_events_error *error);
@@ -107,4 +115,6 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
 
 int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
 
+int perf_pmu__caps_parse(struct perf_pmu *pmu);
+
 #endif /* __PMU_H */
index 83212c65848bb244ad9128494b7833acaf0d3c22..75a9b1d62bba7e5f4cd4f23261d561304c2990dd 100644 (file)
@@ -801,7 +801,7 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel,
 
 static void pyrf_evsel__delete(struct pyrf_evsel *pevsel)
 {
-       perf_evsel__exit(&pevsel->evsel);
+       evsel__exit(&pevsel->evsel);
        Py_TYPE(pevsel)->tp_free((PyObject*)pevsel);
 }
 
@@ -1044,7 +1044,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
 
                pevent->evsel = evsel;
 
-               err = perf_evsel__parse_sample(evsel, event, &pevent->sample);
+               err = evsel__parse_sample(evsel, event, &pevent->sample);
 
                /* Consume the even only after we parsed it out. */
                perf_mmap__consume(&md->core);
index 7def661685032811c11e059bc080deda6f0f161a..a4cc11592f6b38ebf1fb86a31da6e9920b5c6187 100644 (file)
 #include <subcmd/parse-options.h>
 #include <perf/cpumap.h>
 #include "cloexec.h"
+#include "util/perf_api_probe.h"
 #include "record.h"
 #include "../perf-sys.h"
 
-typedef void (*setup_probe_fn_t)(struct evsel *evsel);
-
-static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
+/*
+ * evsel__config_leader_sampling() uses special rules for leader sampling.
+ * However, if the leader is an AUX area event, then assume the event to sample
+ * is the next event.
+ */
+static struct evsel *evsel__read_sampler(struct evsel *evsel, struct evlist *evlist)
 {
-       struct evlist *evlist;
-       struct evsel *evsel;
-       unsigned long flags = perf_event_open_cloexec_flag();
-       int err = -EAGAIN, fd;
-       static pid_t pid = -1;
-
-       evlist = evlist__new();
-       if (!evlist)
-               return -ENOMEM;
-
-       if (parse_events(evlist, str, NULL))
-               goto out_delete;
-
-       evsel = evlist__first(evlist);
+       struct evsel *leader = evsel->leader;
 
-       while (1) {
-               fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
-               if (fd < 0) {
-                       if (pid == -1 && errno == EACCES) {
-                               pid = 0;
-                               continue;
-                       }
-                       goto out_delete;
+       if (evsel__is_aux_event(leader)) {
+               evlist__for_each_entry(evlist, evsel) {
+                       if (evsel->leader == leader && evsel != evsel->leader)
+                               return evsel;
                }
-               break;
-       }
-       close(fd);
-
-       fn(evsel);
-
-       fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags);
-       if (fd < 0) {
-               if (errno == EINVAL)
-                       err = -EINVAL;
-               goto out_delete;
        }
-       close(fd);
-       err = 0;
-
-out_delete:
-       evlist__delete(evlist);
-       return err;
-}
-
-static bool perf_probe_api(setup_probe_fn_t fn)
-{
-       const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
-       struct perf_cpu_map *cpus;
-       int cpu, ret, i = 0;
-
-       cpus = perf_cpu_map__new(NULL);
-       if (!cpus)
-               return false;
-       cpu = cpus->map[0];
-       perf_cpu_map__put(cpus);
-
-       do {
-               ret = perf_do_probe_api(fn, cpu, try[i++]);
-               if (!ret)
-                       return true;
-       } while (ret == -EAGAIN && try[i]);
 
-       return false;
+       return leader;
 }
 
-static void perf_probe_sample_identifier(struct evsel *evsel)
+static void evsel__config_leader_sampling(struct evsel *evsel, struct evlist *evlist)
 {
-       evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
-}
-
-static void perf_probe_comm_exec(struct evsel *evsel)
-{
-       evsel->core.attr.comm_exec = 1;
-}
-
-static void perf_probe_context_switch(struct evsel *evsel)
-{
-       evsel->core.attr.context_switch = 1;
-}
-
-bool perf_can_sample_identifier(void)
-{
-       return perf_probe_api(perf_probe_sample_identifier);
-}
+       struct perf_event_attr *attr = &evsel->core.attr;
+       struct evsel *leader = evsel->leader;
+       struct evsel *read_sampler;
 
-static bool perf_can_comm_exec(void)
-{
-       return perf_probe_api(perf_probe_comm_exec);
-}
+       if (!leader->sample_read)
+               return;
 
-bool perf_can_record_switch_events(void)
-{
-       return perf_probe_api(perf_probe_context_switch);
-}
+       read_sampler = evsel__read_sampler(evsel, evlist);
 
-bool perf_can_record_cpu_wide(void)
-{
-       struct perf_event_attr attr = {
-               .type = PERF_TYPE_SOFTWARE,
-               .config = PERF_COUNT_SW_CPU_CLOCK,
-               .exclude_kernel = 1,
-       };
-       struct perf_cpu_map *cpus;
-       int cpu, fd;
-
-       cpus = perf_cpu_map__new(NULL);
-       if (!cpus)
-               return false;
-       cpu = cpus->map[0];
-       perf_cpu_map__put(cpus);
+       if (evsel == read_sampler)
+               return;
 
-       fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
-       if (fd < 0)
-               return false;
-       close(fd);
-
-       return true;
-}
-
-/*
- * Architectures are expected to know if AUX area sampling is supported by the
- * hardware. Here we check for kernel support.
- */
-bool perf_can_aux_sample(void)
-{
-       struct perf_event_attr attr = {
-               .size = sizeof(struct perf_event_attr),
-               .exclude_kernel = 1,
-               /*
-                * Non-zero value causes the kernel to calculate the effective
-                * attribute size up to that byte.
-                */
-               .aux_sample_size = 1,
-       };
-       int fd;
-
-       fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
        /*
-        * If the kernel attribute is big enough to contain aux_sample_size
-        * then we assume that it is supported. We are relying on the kernel to
-        * validate the attribute size before anything else that could be wrong.
+        * Disable sampling for all group members other than the leader in
+        * case the leader 'leads' the sampling, except when the leader is an
+        * AUX area event, in which case the 2nd event in the group is the one
+        * that 'leads' the sampling.
         */
-       if (fd < 0 && errno == E2BIG)
-               return false;
-       if (fd >= 0)
-               close(fd);
+       attr->freq           = 0;
+       attr->sample_freq    = 0;
+       attr->sample_period  = 0;
+       attr->write_backward = 0;
 
-       return true;
+       /*
+        * We don't get a sample for slave events, we make them when delivering
+        * the group leader sample. Set the slave event to follow the master
+        * sample_type to ease up reporting.
+        * An AUX area event also has sample_type requirements, so also include
+        * the sample type bits from the leader's sample_type to cover that
+        * case.
+        */
+       attr->sample_type = read_sampler->core.attr.sample_type |
+                           leader->core.attr.sample_type;
 }
 
 void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
@@ -188,11 +91,15 @@ void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
        use_comm_exec = perf_can_comm_exec();
 
        evlist__for_each_entry(evlist, evsel) {
-               perf_evsel__config(evsel, opts, callchain);
+               evsel__config(evsel, opts, callchain);
                if (evsel->tracking && use_comm_exec)
                        evsel->core.attr.comm_exec = 1;
        }
 
+       /* Configure leader sampling here now that the sample type is known */
+       evlist__for_each_entry(evlist, evsel)
+               evsel__config_leader_sampling(evsel, evlist);
+
        if (opts->full_auxtrace) {
                /*
                 * Need to be able to synthesize and parse selected events with
@@ -215,7 +122,7 @@ void perf_evlist__config(struct evlist *evlist, struct record_opts *opts,
 
        if (sample_id) {
                evlist__for_each_entry(evlist, evsel)
-                       perf_evsel__set_sample_id(evsel, use_sample_identifier);
+                       evsel__set_sample_id(evsel, use_sample_identifier);
        }
 
        perf_evlist__set_id_pos(evlist);
index 24316458be203e482dfeb40ebc4f5e29145353ed..923565c3b155cdea32e4fa00e28b04c86274f9fc 100644 (file)
@@ -68,6 +68,7 @@ struct record_opts {
        int           affinity;
        int           mmap_flush;
        unsigned int  comp_level;
+       unsigned int  nr_threads_synthesize;
 };
 
 extern const char * const *record_usage;
index d4356030b504f25e6f56795597ab59ee1158cbd3..f55ca07f3ca12d912fcc4c12bb02d925c3eafe71 100644 (file)
@@ -11,6 +11,7 @@
 
 #define        S390_CPUMCF_DIAG_DEF    0xfeef  /* Counter diagnostic entry ID */
 #define        PERF_EVENT_CPUM_CF_DIAG 0xBC000 /* Event: Counter sets */
+#define PERF_EVENT_CPUM_SF_DIAG        0xBD000 /* Event: Combined-sampling */
 
 struct cf_ctrset_entry {       /* CPU-M CF counter set entry (8 byte) */
        unsigned int def:16;    /* 0-15  Data Entry Format */
index 6785cd87aa4db6dacaca94918bbda80f810f4377..f8861998e5bd2645119dbd5c26889dd0adb6f390 100644 (file)
@@ -1047,6 +1047,14 @@ static void s390_cpumsf_free(struct perf_session *session)
        free(sf);
 }
 
+static bool
+s390_cpumsf_evsel_is_auxtrace(struct perf_session *session __maybe_unused,
+                             struct evsel *evsel)
+{
+       return evsel->core.attr.type == PERF_TYPE_RAW &&
+              evsel->core.attr.config == PERF_EVENT_CPUM_SF_DIAG;
+}
+
 static int s390_cpumsf_get_type(const char *cpuid)
 {
        int ret, family = 0;
@@ -1071,7 +1079,8 @@ static bool check_auxtrace_itrace(struct itrace_synth_opts *itops)
                itops->pwr_events || itops->errors ||
                itops->dont_decode || itops->calls || itops->returns ||
                itops->callchain || itops->thread_stack ||
-               itops->last_branch;
+               itops->last_branch || itops->add_callchain ||
+               itops->add_last_branch;
        if (!ison)
                return true;
        pr_err("Unsupported --itrace options specified\n");
@@ -1142,6 +1151,7 @@ int s390_cpumsf_process_auxtrace_info(union perf_event *event,
        sf->auxtrace.flush_events = s390_cpumsf_flush;
        sf->auxtrace.free_events = s390_cpumsf_free_events;
        sf->auxtrace.free = s390_cpumsf_free;
+       sf->auxtrace.evsel_is_auxtrace = s390_cpumsf_evsel_is_auxtrace;
        session->auxtrace = &sf->auxtrace;
 
        if (dump_trace)
index 2c372cf5495e5537c6948ff62f87b12313e91a61..739516fdf6e38c16a84d7a7ce3c83543bfc61cd3 100644 (file)
@@ -741,7 +741,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
        if (!dict_sample)
                Py_FatalError("couldn't create Python dictionary");
 
-       pydict_set_item_string_decref(dict, "ev_name", _PyUnicode_FromString(perf_evsel__name(evsel)));
+       pydict_set_item_string_decref(dict, "ev_name", _PyUnicode_FromString(evsel__name(evsel)));
        pydict_set_item_string_decref(dict, "attr", _PyBytes_FromStringAndSize((const char *)&evsel->core.attr, sizeof(evsel->core.attr)));
 
        pydict_set_item_string_decref(dict_sample, "pid",
@@ -968,7 +968,7 @@ static int python_export_evsel(struct db_export *dbe, struct evsel *evsel)
        t = tuple_new(2);
 
        tuple_set_u64(t, 0, evsel->db_id);
-       tuple_set_string(t, 1, perf_evsel__name(evsel));
+       tuple_set_string(t, 1, evsel__name(evsel));
 
        call_object(tables->evsel_handler, t, "evsel_table");
 
@@ -1349,7 +1349,7 @@ static void get_handler_name(char *str, size_t size,
 {
        char *p = str;
 
-       scnprintf(str, size, "stat__%s", perf_evsel__name(evsel));
+       scnprintf(str, size, "stat__%s", evsel__name(evsel));
 
        while ((p = strchr(p, ':'))) {
                *p = '_';
index 0b0bfe5bef17cd8cb86029e6aca07f9dccc0c72b..c11d89e0ee55a317b090693279afeeed14cb823c 100644 (file)
@@ -1059,7 +1059,7 @@ static void callchain__printf(struct evsel *evsel,
        unsigned int i;
        struct ip_callchain *callchain = sample->callchain;
 
-       if (perf_evsel__has_branch_callstack(evsel))
+       if (evsel__has_branch_callstack(evsel))
                callchain__lbr_callstack_printf(sample);
 
        printf("... FP chain: nr:%" PRIu64 "\n", callchain->nr);
@@ -1243,8 +1243,8 @@ static void dump_sample(struct evsel *evsel, union perf_event *event,
        if (evsel__has_callchain(evsel))
                callchain__printf(evsel, sample);
 
-       if (sample_type & PERF_SAMPLE_BRANCH_STACK)
-               branch_stack__printf(sample, perf_evsel__has_branch_callstack(evsel));
+       if (evsel__has_br_stack(evsel))
+               branch_stack__printf(sample, evsel__has_branch_callstack(evsel));
 
        if (sample_type & PERF_SAMPLE_REGS_USER)
                regs_user__printf(sample);
@@ -1280,8 +1280,7 @@ static void dump_read(struct evsel *evsel, union perf_event *event)
                return;
 
        printf(": %d %d %s %" PRI_lu64 "\n", event->read.pid, event->read.tid,
-              perf_evsel__name(evsel),
-              event->read.value);
+              evsel__name(evsel), event->read.value);
 
        if (!evsel)
                return;
diff --git a/tools/perf/util/sideband_evlist.c b/tools/perf/util/sideband_evlist.c
new file mode 100644 (file)
index 0000000..1580a3c
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "util/debug.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
+#include "util/mmap.h"
+#include "util/perf_api_probe.h"
+#include <perf/mmap.h>
+#include <linux/perf_event.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdbool.h>
+
+int perf_evlist__add_sb_event(struct evlist *evlist, struct perf_event_attr *attr,
+                             evsel__sb_cb_t cb, void *data)
+{
+       struct evsel *evsel;
+
+       if (!attr->sample_id_all) {
+               pr_warning("enabling sample_id_all for all side band events\n");
+               attr->sample_id_all = 1;
+       }
+
+       evsel = perf_evsel__new_idx(attr, evlist->core.nr_entries);
+       if (!evsel)
+               return -1;
+
+       evsel->side_band.cb = cb;
+       evsel->side_band.data = data;
+       evlist__add(evlist, evsel);
+       return 0;
+}
+
+static void *perf_evlist__poll_thread(void *arg)
+{
+       struct evlist *evlist = arg;
+       bool draining = false;
+       int i, done = 0;
+       /*
+        * In order to read symbols from other namespaces perf to needs to call
+        * setns(2).  This isn't permitted if the struct_fs has multiple users.
+        * unshare(2) the fs so that we may continue to setns into namespaces
+        * that we're observing when, for instance, reading the build-ids at
+        * the end of a 'perf record' session.
+        */
+       unshare(CLONE_FS);
+
+       while (!done) {
+               bool got_data = false;
+
+               if (evlist->thread.done)
+                       draining = true;
+
+               if (!draining)
+                       evlist__poll(evlist, 1000);
+
+               for (i = 0; i < evlist->core.nr_mmaps; i++) {
+                       struct mmap *map = &evlist->mmap[i];
+                       union perf_event *event;
+
+                       if (perf_mmap__read_init(&map->core))
+                               continue;
+                       while ((event = perf_mmap__read_event(&map->core)) != NULL) {
+                               struct evsel *evsel = perf_evlist__event2evsel(evlist, event);
+
+                               if (evsel && evsel->side_band.cb)
+                                       evsel->side_band.cb(event, evsel->side_band.data);
+                               else
+                                       pr_warning("cannot locate proper evsel for the side band event\n");
+
+                               perf_mmap__consume(&map->core);
+                               got_data = true;
+                       }
+                       perf_mmap__read_done(&map->core);
+               }
+
+               if (draining && !got_data)
+                       break;
+       }
+       return NULL;
+}
+
+void evlist__set_cb(struct evlist *evlist, evsel__sb_cb_t cb, void *data)
+{
+       struct evsel *evsel;
+
+       evlist__for_each_entry(evlist, evsel) {
+               evsel->core.attr.sample_id_all    = 1;
+               evsel->core.attr.watermark        = 1;
+               evsel->core.attr.wakeup_watermark = 1;
+               evsel->side_band.cb   = cb;
+               evsel->side_band.data = data;
+      }
+}
+
+int perf_evlist__start_sb_thread(struct evlist *evlist, struct target *target)
+{
+       struct evsel *counter;
+
+       if (!evlist)
+               return 0;
+
+       if (perf_evlist__create_maps(evlist, target))
+               goto out_delete_evlist;
+
+       if (evlist->core.nr_entries > 1) {
+               bool can_sample_identifier = perf_can_sample_identifier();
+
+               evlist__for_each_entry(evlist, counter)
+                       evsel__set_sample_id(counter, can_sample_identifier);
+
+               perf_evlist__set_id_pos(evlist);
+       }
+
+       evlist__for_each_entry(evlist, counter) {
+               if (evsel__open(counter, evlist->core.cpus, evlist->core.threads) < 0)
+                       goto out_delete_evlist;
+       }
+
+       if (evlist__mmap(evlist, UINT_MAX))
+               goto out_delete_evlist;
+
+       evlist__for_each_entry(evlist, counter) {
+               if (evsel__enable(counter))
+                       goto out_delete_evlist;
+       }
+
+       evlist->thread.done = 0;
+       if (pthread_create(&evlist->thread.th, NULL, perf_evlist__poll_thread, evlist))
+               goto out_delete_evlist;
+
+       return 0;
+
+out_delete_evlist:
+       evlist__delete(evlist);
+       evlist = NULL;
+       return -1;
+}
+
+void perf_evlist__stop_sb_thread(struct evlist *evlist)
+{
+       if (!evlist)
+               return;
+       evlist->thread.done = 1;
+       pthread_join(evlist->thread.th, NULL);
+       evlist__delete(evlist);
+}
index 3b791ef2cd502222ff01a2edb0605a8c74a46bf7..20bacd5972adec4425ad11bef211a9dee5b6d45d 100644 (file)
@@ -15,6 +15,9 @@ int smt_on(void)
        if (cached)
                return cached_result;
 
+       if (sysfs__read_int("devices/system/cpu/smt/active", &cached_result) > 0)
+               goto done;
+
        ncpu = sysconf(_SC_NPROCESSORS_CONF);
        for (cpu = 0; cpu < ncpu; cpu++) {
                unsigned long long siblings;
@@ -24,13 +27,13 @@ int smt_on(void)
 
                snprintf(fn, sizeof fn,
                        "devices/system/cpu/cpu%d/topology/core_cpus", cpu);
-               if (access(fn, F_OK) == -1) {
+               if (sysfs__read_str(fn, &str, &strlen) < 0) {
                        snprintf(fn, sizeof fn,
                                "devices/system/cpu/cpu%d/topology/thread_siblings",
                                cpu);
+                       if (sysfs__read_str(fn, &str, &strlen) < 0)
+                               continue;
                }
-               if (sysfs__read_str(fn, &str, &strlen) < 0)
-                       continue;
                /* Entry is hex, but does not have 0x, so need custom parser */
                siblings = strtoull(str, NULL, 16);
                free(str);
@@ -42,6 +45,7 @@ int smt_on(void)
        }
        if (!cached) {
                cached_result = 0;
+done:
                cached = true;
        }
        return cached_result;
index f14cc728c358a84c016ac3951cc147571b19902e..c1f8879f92cca419b3d44941caf6f8580f3de9be 100644 (file)
@@ -237,7 +237,7 @@ static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
        return (int64_t)(right_ip - left_ip);
 }
 
-static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
+int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 {
        if (!sym_l || !sym_r)
                return cmp_null(sym_l, sym_r);
@@ -300,8 +300,14 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
 
        if (verbose > 0) {
                char o = map ? dso__symtab_origin(map->dso) : '!';
+               u64 rip = ip;
+
+               if (map && map->dso && map->dso->kernel
+                   && map->dso->adjust_symbols)
+                       rip = map->unmap_ip(map, ip);
+
                ret += repsep_snprintf(bf, size, "%-#*llx %c ",
-                                      BITS_PER_LONG / 4 + 2, ip, o);
+                                      BITS_PER_LONG / 4 + 2, rip, o);
        }
 
        ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
@@ -2354,7 +2360,7 @@ static struct evsel *find_evsel(struct evlist *evlist, char *event_name)
 
                evsel = evlist__first(evlist);
                while (--nr > 0)
-                       evsel = perf_evsel__next(evsel);
+                       evsel = evsel__next(evsel);
 
                return evsel;
        }
index cfa6ac6f7d06da5580542767ff04800ee61c8583..66d39c4cfe2b3213344720b5460b04de6edd69f7 100644 (file)
@@ -311,5 +311,7 @@ int64_t
 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t
 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right);
+int64_t
+_sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r);
 char *hist_entry__srcline(struct hist_entry *he);
 #endif /* __PERF_SORT_H */
index 9e757d18d713bbff1aeb0cacd3c1d478c95699f5..3c6976f7574c3c30a78b097ec7bf2829e5855f5f 100644 (file)
@@ -237,8 +237,6 @@ static bool valid_only_metric(const char *unit)
        if (!unit)
                return false;
        if (strstr(unit, "/sec") ||
-           strstr(unit, "hz") ||
-           strstr(unit, "Hz") ||
            strstr(unit, "CPUs utilized"))
                return false;
        return true;
@@ -248,7 +246,7 @@ static const char *fixunit(char *buf, struct evsel *evsel,
                           const char *unit)
 {
        if (!strncmp(unit, "of all", 6)) {
-               snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel),
+               snprintf(buf, 1024, "%s %s", evsel__name(evsel),
                         unit);
                return buf;
        }
@@ -335,7 +333,7 @@ static int first_shadow_cpu(struct perf_stat_config *config,
        if (config->aggr_mode == AGGR_GLOBAL)
                return 0;
 
-       for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) {
+       for (i = 0; i < evsel__nr_cpus(evsel); i++) {
                int cpu2 = evsel__cpus(evsel)->map[i];
 
                if (config->aggr_get_id(config, evlist->core.cpus, cpu2) == id)
@@ -369,7 +367,7 @@ static void abs_printout(struct perf_stat_config *config,
                        config->csv_output ? 0 : config->unit_width,
                        evsel->unit, config->csv_sep);
 
-       fprintf(output, "%-*s", config->csv_output ? 0 : 25, perf_evsel__name(evsel));
+       fprintf(output, "%-*s", config->csv_output ? 0 : 25, evsel__name(evsel));
 
        print_cgroup(config, evsel);
 }
@@ -463,8 +461,7 @@ static void printout(struct perf_stat_config *config, int id, int nr,
                        counter->unit, config->csv_sep);
 
                fprintf(config->output, "%*s",
-                       config->csv_output ? 0 : -25,
-                       perf_evsel__name(counter));
+                       config->csv_output ? 0 : -25, evsel__name(counter));
 
                print_cgroup(config, counter);
 
@@ -510,7 +507,7 @@ static void aggr_update_shadow(struct perf_stat_config *config,
                id = config->aggr_map->map[s];
                evlist__for_each_entry(evlist, counter) {
                        val = 0;
-                       for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
+                       for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
                                s2 = config->aggr_get_id(config, evlist->core.cpus, cpu);
                                if (s2 != id)
                                        continue;
@@ -561,11 +558,11 @@ static void collect_all_aliases(struct perf_stat_config *config, struct evsel *c
 
        alias = list_prepare_entry(counter, &(evlist->core.entries), core.node);
        list_for_each_entry_continue (alias, &evlist->core.entries, core.node) {
-               if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) ||
+               if (strcmp(evsel__name(alias), evsel__name(counter)) ||
                    alias->scale != counter->scale ||
                    alias->cgrp != counter->cgrp ||
                    strcmp(alias->unit, counter->unit) ||
-                   perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter) ||
+                   evsel__is_clock(alias) != evsel__is_clock(counter) ||
                    !strcmp(alias->pmu_name, counter->pmu_name))
                        break;
                alias->merged_stat = true;
@@ -601,7 +598,7 @@ static void aggr_cb(struct perf_stat_config *config,
        struct aggr_data *ad = data;
        int cpu, s2;
 
-       for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
+       for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
                struct perf_counts_values *counts;
 
                s2 = config->aggr_get_id(config, evsel__cpus(counter), cpu);
@@ -849,7 +846,7 @@ static void print_counter(struct perf_stat_config *config,
        double uval;
        int cpu;
 
-       for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
+       for (cpu = 0; cpu < evsel__nr_cpus(counter); cpu++) {
                struct aggr_data ad = { .cpu = cpu };
 
                if (!collect_data(config, counter, counter_cb, &ad))
@@ -1150,7 +1147,7 @@ static void print_percore_thread(struct perf_stat_config *config,
        int s, s2, id;
        bool first = true;
 
-       for (int i = 0; i < perf_evsel__nr_cpus(counter); i++) {
+       for (int i = 0; i < evsel__nr_cpus(counter); i++) {
                s2 = config->aggr_get_id(config, evsel__cpus(counter), i);
                for (s = 0; s < config->aggr_map->nr; s++) {
                        id = config->aggr_map->map[s];
index 03ecb8cd0eeca97c3769d3480575d6d4af13f349..129b8c5f2538925245f57a727851e9ce24bbe08e 100644 (file)
@@ -216,9 +216,9 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
 
        count *= counter->scale;
 
-       if (perf_evsel__is_clock(counter))
+       if (evsel__is_clock(counter))
                update_runtime_stat(st, STAT_NSECS, 0, cpu, count_ns);
-       else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
+       else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
                update_runtime_stat(st, STAT_CYCLES, ctx, cpu, count);
        else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
                update_runtime_stat(st, STAT_CYCLES_IN_TX, ctx, cpu, count);
@@ -241,25 +241,25 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
        else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
                update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
                                    ctx, cpu, count);
-       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
+       else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
                update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
                                    ctx, cpu, count);
-       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
+       else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
                update_runtime_stat(st, STAT_STALLED_CYCLES_BACK,
                                    ctx, cpu, count);
-       else if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
+       else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
                update_runtime_stat(st, STAT_BRANCHES, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
+       else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
                update_runtime_stat(st, STAT_CACHEREFS, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
+       else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
                update_runtime_stat(st, STAT_L1_DCACHE, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
+       else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
                update_runtime_stat(st, STAT_L1_ICACHE, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_LL))
+       else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL))
                update_runtime_stat(st, STAT_LL_CACHE, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
+       else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
                update_runtime_stat(st, STAT_DTLB_CACHE, ctx, cpu, count);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
+       else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
                update_runtime_stat(st, STAT_ITLB_CACHE, ctx, cpu, count);
        else if (perf_stat_evsel__is(counter, SMI_NUM))
                update_runtime_stat(st, STAT_SMI_NUM, ctx, cpu, count);
@@ -336,7 +336,7 @@ void perf_stat__collect_metric_expr(struct evlist *evsel_list)
                metric_events = counter->metric_events;
                if (!metric_events) {
                        if (expr__find_other(counter->metric_expr, counter->name,
-                                               &metric_names, &num_metric_names) < 0)
+                                               &metric_names, &num_metric_names, 1) < 0)
                                continue;
 
                        metric_events = calloc(sizeof(struct evsel *),
@@ -723,13 +723,14 @@ static void generic_metric(struct perf_stat_config *config,
                           char *name,
                           const char *metric_name,
                           const char *metric_unit,
+                          int runtime,
                           double avg,
                           int cpu,
                           struct perf_stat_output_ctx *out,
                           struct runtime_stat *st)
 {
        print_metric_t print_metric = out->print_metric;
-       struct parse_ctx pctx;
+       struct expr_parse_ctx pctx;
        double ratio, scale;
        int i;
        void *ctxp = out->ctx;
@@ -777,7 +778,7 @@ static void generic_metric(struct perf_stat_config *config,
        }
 
        if (!metric_events[i]) {
-               if (expr__parse(&ratio, &pctx, metric_expr) == 0) {
+               if (expr__parse(&ratio, &pctx, metric_expr, runtime) == 0) {
                        char *unit;
                        char metric_bf[64];
 
@@ -786,9 +787,13 @@ static void generic_metric(struct perf_stat_config *config,
                                        &unit, &scale) >= 0) {
                                        ratio *= scale;
                                }
-
-                               scnprintf(metric_bf, sizeof(metric_bf),
+                               if (strstr(metric_expr, "?"))
+                                       scnprintf(metric_bf, sizeof(metric_bf),
+                                         "%s  %s_%d", unit, metric_name, runtime);
+                               else
+                                       scnprintf(metric_bf, sizeof(metric_bf),
                                          "%s  %s", unit, metric_name);
+
                                print_metric(config, ctxp, NULL, "%8.1f",
                                             metric_bf, ratio);
                        } else {
@@ -828,7 +833,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
        struct metric_event *me;
        int num = 1;
 
-       if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
+       if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
                total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
 
                if (total) {
@@ -853,7 +858,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        "stalled cycles per insn",
                                        ratio);
                }
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
+       } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
                if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0)
                        print_branch_misses(config, cpu, evsel, avg, out, st);
                else
@@ -908,7 +913,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        print_ll_cache_misses(config, cpu, evsel, avg, out, st);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all LL-cache hits", 0);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
+       } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
                total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu);
 
                if (total)
@@ -919,11 +924,11 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                     "of all cache refs", ratio);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
+       } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
                print_stalled_cycles_frontend(config, cpu, evsel, avg, out, st);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
+       } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
                print_stalled_cycles_backend(config, cpu, evsel, avg, out, st);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
+       } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
                total = runtime_stat_avg(st, STAT_NSECS, 0, cpu);
 
                if (total) {
@@ -974,7 +979,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        ratio = total / avg;
 
                print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio);
-       } else if (perf_evsel__is_clock(evsel)) {
+       } else if (evsel__is_clock(evsel)) {
                if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0)
                        print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized",
                                     avg / (ratio * evsel->scale));
@@ -1022,7 +1027,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        print_metric(config, ctxp, NULL, NULL, name, 0);
        } else if (evsel->metric_expr) {
                generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name,
-                               evsel->metric_name, NULL, avg, cpu, out, st);
+                               evsel->metric_name, NULL, 1, avg, cpu, out, st);
        } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) {
                char unit = 'M';
                char unit_buf[10];
@@ -1051,7 +1056,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                out->new_line(config, ctxp);
                        generic_metric(config, mexp->metric_expr, mexp->metric_events,
                                        evsel->name, mexp->metric_name,
-                                       mexp->metric_unit, avg, cpu, out, st);
+                                       mexp->metric_unit, mexp->runtime, avg, cpu, out, st);
                }
        }
        if (num == 0)
index 5f26137b8d6028fa0fc00e772ebacf43ba3e007e..774468341851c3c52837a283a1ace08cba766841 100644 (file)
@@ -108,7 +108,7 @@ static void perf_stat_evsel_id_init(struct evsel *evsel)
        /* ps->id is 0 hence PERF_STAT_EVSEL_ID__NONE by default */
 
        for (i = 0; i < PERF_STAT_EVSEL_ID__MAX; i++) {
-               if (!strcmp(perf_evsel__name(evsel), id_str[i])) {
+               if (!strcmp(evsel__name(evsel), id_str[i])) {
                        ps->id = i;
                        break;
                }
@@ -173,7 +173,7 @@ static void perf_evsel__reset_prev_raw_counts(struct evsel *evsel)
 
 static int perf_evsel__alloc_stats(struct evsel *evsel, bool alloc_raw)
 {
-       int ncpus = perf_evsel__nr_cpus(evsel);
+       int ncpus = evsel__nr_cpus(evsel);
        int nthreads = perf_thread_map__nr(evsel->core.threads);
 
        if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
@@ -302,7 +302,7 @@ process_counter_values(struct perf_stat_config *config, struct evsel *evsel,
        case AGGR_NODE:
        case AGGR_NONE:
                if (!evsel->snapshot)
-                       perf_evsel__compute_deltas(evsel, cpu, thread, count);
+                       evsel__compute_deltas(evsel, cpu, thread, count);
                perf_counts_values__scale(count, config->scale, NULL);
                if ((config->aggr_mode == AGGR_NONE) && (!evsel->percore)) {
                        perf_stat__update_shadow_stats(evsel, count->val,
@@ -334,7 +334,7 @@ static int process_counter_maps(struct perf_stat_config *config,
                                struct evsel *counter)
 {
        int nthreads = perf_thread_map__nr(counter->core.threads);
-       int ncpus = perf_evsel__nr_cpus(counter);
+       int ncpus = evsel__nr_cpus(counter);
        int cpu, thread;
 
        if (counter->core.system_wide)
@@ -368,8 +368,10 @@ int perf_stat_process_counter(struct perf_stat_config *config,
         * interval mode, otherwise overall avg running
         * averages will be shown for each interval.
         */
-       if (config->interval)
-               init_stats(ps->res_stats);
+       if (config->interval) {
+               for (i = 0; i < 3; i++)
+                       init_stats(&ps->res_stats[i]);
+       }
 
        if (counter->per_pkg)
                zero_per_pkg(counter);
@@ -382,7 +384,7 @@ int perf_stat_process_counter(struct perf_stat_config *config,
                return 0;
 
        if (!counter->snapshot)
-               perf_evsel__compute_deltas(counter, -1, -1, aggr);
+               evsel__compute_deltas(counter, -1, -1, aggr);
        perf_counts_values__scale(aggr, config->scale, &counter->counts->scaled);
 
        for (i = 0; i < 3; i++)
@@ -390,7 +392,7 @@ int perf_stat_process_counter(struct perf_stat_config *config,
 
        if (verbose > 0) {
                fprintf(config->output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
-                       perf_evsel__name(counter), count[0], count[1], count[2]);
+                       evsel__name(counter), count[0], count[1], count[2]);
        }
 
        /*
@@ -507,7 +509,7 @@ int create_perf_stat_counter(struct evsel *evsel,
         * either manually by us or by kernel via enable_on_exec
         * set later.
         */
-       if (perf_evsel__is_group_leader(evsel)) {
+       if (evsel__is_group_leader(evsel)) {
                attr->disabled = 1;
 
                /*
@@ -519,7 +521,7 @@ int create_perf_stat_counter(struct evsel *evsel,
        }
 
        if (target__has_cpu(target) && !target__has_per_thread(target))
-               return perf_evsel__open_per_cpu(evsel, evsel__cpus(evsel), cpu);
+               return evsel__open_per_cpu(evsel, evsel__cpus(evsel), cpu);
 
-       return perf_evsel__open_per_thread(evsel, evsel->core.threads);
+       return evsel__open_per_thread(evsel, evsel->core.threads);
 }
index 26bc6a0096ce568bd4e9e70fa910063f1633787b..381da6b39f89a46f0bf179a0811dc2aa7e511c8b 100644 (file)
@@ -566,6 +566,20 @@ void dso__sort_by_name(struct dso *dso)
        return symbols__sort_by_name(&dso->symbol_names, &dso->symbols);
 }
 
+/*
+ * While we find nice hex chars, build a long_val.
+ * Return number of chars processed.
+ */
+static int hex2u64(const char *ptr, u64 *long_val)
+{
+       char *p;
+
+       *long_val = strtoull(ptr, &p, 16);
+
+       return p - ptr;
+}
+
+
 int modules__parse(const char *filename, void *arg,
                   int (*process_module)(void *arg, const char *name,
                                         u64 start, u64 size))
@@ -1544,6 +1558,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
                return true;
 
        case DSO_BINARY_TYPE__BPF_PROG_INFO:
+       case DSO_BINARY_TYPE__BPF_IMAGE:
        case DSO_BINARY_TYPE__NOT_FOUND:
        default:
                return false;
index a661b122d9d8f25836bd12943e119c6da3c26c9f..89b390623b63d610e1d3730e236edd38090f8f01 100644 (file)
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */
 #include <api/fs/fs.h>
+#include <api/io.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -71,7 +72,6 @@ int perf_tool__process_synth_event(struct perf_tool *tool,
 static int perf_event__get_comm_ids(pid_t pid, char *comm, size_t len,
                                    pid_t *tgid, pid_t *ppid)
 {
-       char filename[PATH_MAX];
        char bf[4096];
        int fd;
        size_t size = 0;
@@ -81,11 +81,11 @@ static int perf_event__get_comm_ids(pid_t pid, char *comm, size_t len,
        *tgid = -1;
        *ppid = -1;
 
-       snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+       snprintf(bf, sizeof(bf), "/proc/%d/status", pid);
 
-       fd = open(filename, O_RDONLY);
+       fd = open(bf, O_RDONLY);
        if (fd < 0) {
-               pr_debug("couldn't open %s\n", filename);
+               pr_debug("couldn't open %s\n", bf);
                return -1;
        }
 
@@ -274,6 +274,79 @@ static int perf_event__synthesize_fork(struct perf_tool *tool,
        return 0;
 }
 
+static bool read_proc_maps_line(struct io *io, __u64 *start, __u64 *end,
+                               u32 *prot, u32 *flags, __u64 *offset,
+                               u32 *maj, u32 *min,
+                               __u64 *inode,
+                               ssize_t pathname_size, char *pathname)
+{
+       __u64 temp;
+       int ch;
+       char *start_pathname = pathname;
+
+       if (io__get_hex(io, start) != '-')
+               return false;
+       if (io__get_hex(io, end) != ' ')
+               return false;
+
+       /* map protection and flags bits */
+       *prot = 0;
+       ch = io__get_char(io);
+       if (ch == 'r')
+               *prot |= PROT_READ;
+       else if (ch != '-')
+               return false;
+       ch = io__get_char(io);
+       if (ch == 'w')
+               *prot |= PROT_WRITE;
+       else if (ch != '-')
+               return false;
+       ch = io__get_char(io);
+       if (ch == 'x')
+               *prot |= PROT_EXEC;
+       else if (ch != '-')
+               return false;
+       ch = io__get_char(io);
+       if (ch == 's')
+               *flags = MAP_SHARED;
+       else if (ch == 'p')
+               *flags = MAP_PRIVATE;
+       else
+               return false;
+       if (io__get_char(io) != ' ')
+               return false;
+
+       if (io__get_hex(io, offset) != ' ')
+               return false;
+
+       if (io__get_hex(io, &temp) != ':')
+               return false;
+       *maj = temp;
+       if (io__get_hex(io, &temp) != ' ')
+               return false;
+       *min = temp;
+
+       ch = io__get_dec(io, inode);
+       if (ch != ' ') {
+               *pathname = '\0';
+               return ch == '\n';
+       }
+       do {
+               ch = io__get_char(io);
+       } while (ch == ' ');
+       while (true) {
+               if (ch < 0)
+                       return false;
+               if (ch == '\0' || ch == '\n' ||
+                   (pathname + 1 - start_pathname) >= pathname_size) {
+                       *pathname = '\0';
+                       return true;
+               }
+               *pathname++ = ch;
+               ch = io__get_char(io);
+       }
+}
+
 int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       union perf_event *event,
                                       pid_t pid, pid_t tgid,
@@ -281,9 +354,9 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       struct machine *machine,
                                       bool mmap_data)
 {
-       char filename[PATH_MAX];
-       FILE *fp;
        unsigned long long t;
+       char bf[BUFSIZ];
+       struct io io;
        bool truncation = false;
        unsigned long long timeout = proc_map_timeout * 1000000ULL;
        int rc = 0;
@@ -293,59 +366,52 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
        if (machine__is_default_guest(machine))
                return 0;
 
-       snprintf(filename, sizeof(filename), "%s/proc/%d/task/%d/maps",
-                machine->root_dir, pid, pid);
+       snprintf(bf, sizeof(bf), "%s/proc/%d/task/%d/maps",
+               machine->root_dir, pid, pid);
 
-       fp = fopen(filename, "r");
-       if (fp == NULL) {
+       io.fd = open(bf, O_RDONLY, 0);
+       if (io.fd < 0) {
                /*
                 * We raced with a task exiting - just return:
                 */
-               pr_debug("couldn't open %s\n", filename);
+               pr_debug("couldn't open %s\n", bf);
                return -1;
        }
+       io__init(&io, io.fd, bf, sizeof(bf));
 
        event->header.type = PERF_RECORD_MMAP2;
        t = rdclock();
 
-       while (1) {
-               char bf[BUFSIZ];
-               char prot[5];
-               char execname[PATH_MAX];
-               char anonstr[] = "//anon";
-               unsigned int ino;
+       while (!io.eof) {
+               static const char anonstr[] = "//anon";
                size_t size;
-               ssize_t n;
 
-               if (fgets(bf, sizeof(bf), fp) == NULL)
-                       break;
+               /* ensure null termination since stack will be reused. */
+               event->mmap2.filename[0] = '\0';
+
+               /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
+               if (!read_proc_maps_line(&io,
+                                       &event->mmap2.start,
+                                       &event->mmap2.len,
+                                       &event->mmap2.prot,
+                                       &event->mmap2.flags,
+                                       &event->mmap2.pgoff,
+                                       &event->mmap2.maj,
+                                       &event->mmap2.min,
+                                       &event->mmap2.ino,
+                                       sizeof(event->mmap2.filename),
+                                       event->mmap2.filename))
+                       continue;
 
                if ((rdclock() - t) > timeout) {
-                       pr_warning("Reading %s time out. "
+                       pr_warning("Reading %s/proc/%d/task/%d/maps time out. "
                                   "You may want to increase "
                                   "the time limit by --proc-map-timeout\n",
-                                  filename);
+                                  machine->root_dir, pid, pid);
                        truncation = true;
                        goto out;
                }
 
-               /* ensure null termination since stack will be reused. */
-               strcpy(execname, "");
-
-               /* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */
-               n = sscanf(bf, "%"PRI_lx64"-%"PRI_lx64" %s %"PRI_lx64" %x:%x %u %[^\n]\n",
-                      &event->mmap2.start, &event->mmap2.len, prot,
-                      &event->mmap2.pgoff, &event->mmap2.maj,
-                      &event->mmap2.min,
-                      &ino, execname);
-
-               /*
-                * Anon maps don't have the execname.
-                */
-               if (n < 7)
-                       continue;
-
-               event->mmap2.ino = (u64)ino;
                event->mmap2.ino_generation = 0;
 
                /*
@@ -356,23 +422,8 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                else
                        event->header.misc = PERF_RECORD_MISC_GUEST_USER;
 
-               /* map protection and flags bits */
-               event->mmap2.prot = 0;
-               event->mmap2.flags = 0;
-               if (prot[0] == 'r')
-                       event->mmap2.prot |= PROT_READ;
-               if (prot[1] == 'w')
-                       event->mmap2.prot |= PROT_WRITE;
-               if (prot[2] == 'x')
-                       event->mmap2.prot |= PROT_EXEC;
-
-               if (prot[3] == 's')
-                       event->mmap2.flags |= MAP_SHARED;
-               else
-                       event->mmap2.flags |= MAP_PRIVATE;
-
-               if (prot[2] != 'x') {
-                       if (!mmap_data || prot[0] != 'r')
+               if ((event->mmap2.prot & PROT_EXEC) == 0) {
+                       if (!mmap_data || (event->mmap2.prot & PROT_READ) == 0)
                                continue;
 
                        event->header.misc |= PERF_RECORD_MISC_MMAP_DATA;
@@ -382,17 +433,17 @@ out:
                if (truncation)
                        event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT;
 
-               if (!strcmp(execname, ""))
-                       strcpy(execname, anonstr);
+               if (!strcmp(event->mmap2.filename, ""))
+                       strcpy(event->mmap2.filename, anonstr);
 
                if (hugetlbfs_mnt_len &&
-                   !strncmp(execname, hugetlbfs_mnt, hugetlbfs_mnt_len)) {
-                       strcpy(execname, anonstr);
+                   !strncmp(event->mmap2.filename, hugetlbfs_mnt,
+                            hugetlbfs_mnt_len)) {
+                       strcpy(event->mmap2.filename, anonstr);
                        event->mmap2.flags |= MAP_HUGETLB;
                }
 
-               size = strlen(execname) + 1;
-               memcpy(event->mmap2.filename, execname, size);
+               size = strlen(event->mmap2.filename) + 1;
                size = PERF_ALIGN(size, sizeof(u64));
                event->mmap2.len -= event->mmap.start;
                event->mmap2.header.size = (sizeof(event->mmap2) -
@@ -411,7 +462,7 @@ out:
                        break;
        }
 
-       fclose(fp);
+       close(io.fd);
        return rc;
 }
 
@@ -1130,7 +1181,7 @@ void cpu_map_data__synthesize(struct perf_record_cpu_map_data *data, struct perf
                synthesize_mask((struct perf_record_record_cpu_map *)data->data, map, max);
        default:
                break;
-       };
+       }
 }
 
 static struct perf_record_cpu_map *cpu_map_event__new(struct perf_cpu_map *map)
index 0885967d5bc38077acf6a69cd66718657ca3fed9..1b992bbba4e8e66826a743470b48cdc2c8f6f565 100644 (file)
@@ -80,6 +80,10 @@ struct thread_stack_entry {
  * @comm: current comm
  * @arr_sz: size of array if this is the first element of an array
  * @rstate: used to detect retpolines
+ * @br_stack_rb: branch stack (ring buffer)
+ * @br_stack_sz: maximum branch stack size
+ * @br_stack_pos: current position in @br_stack_rb
+ * @mispred_all: mark all branches as mispredicted
  */
 struct thread_stack {
        struct thread_stack_entry *stack;
@@ -95,6 +99,10 @@ struct thread_stack {
        struct comm *comm;
        unsigned int arr_sz;
        enum retpoline_state_t rstate;
+       struct branch_stack *br_stack_rb;
+       unsigned int br_stack_sz;
+       unsigned int br_stack_pos;
+       bool mispred_all;
 };
 
 /*
@@ -126,13 +134,26 @@ static int thread_stack__grow(struct thread_stack *ts)
 }
 
 static int thread_stack__init(struct thread_stack *ts, struct thread *thread,
-                             struct call_return_processor *crp)
+                             struct call_return_processor *crp,
+                             bool callstack, unsigned int br_stack_sz)
 {
        int err;
 
-       err = thread_stack__grow(ts);
-       if (err)
-               return err;
+       if (callstack) {
+               err = thread_stack__grow(ts);
+               if (err)
+                       return err;
+       }
+
+       if (br_stack_sz) {
+               size_t sz = sizeof(struct branch_stack);
+
+               sz += br_stack_sz * sizeof(struct branch_entry);
+               ts->br_stack_rb = zalloc(sz);
+               if (!ts->br_stack_rb)
+                       return -ENOMEM;
+               ts->br_stack_sz = br_stack_sz;
+       }
 
        if (thread->maps && thread->maps->machine) {
                struct machine *machine = thread->maps->machine;
@@ -150,7 +171,9 @@ static int thread_stack__init(struct thread_stack *ts, struct thread *thread,
 }
 
 static struct thread_stack *thread_stack__new(struct thread *thread, int cpu,
-                                             struct call_return_processor *crp)
+                                             struct call_return_processor *crp,
+                                             bool callstack,
+                                             unsigned int br_stack_sz)
 {
        struct thread_stack *ts = thread->ts, *new_ts;
        unsigned int old_sz = ts ? ts->arr_sz : 0;
@@ -176,7 +199,7 @@ static struct thread_stack *thread_stack__new(struct thread *thread, int cpu,
                ts += cpu;
 
        if (!ts->stack &&
-           thread_stack__init(ts, thread, crp))
+           thread_stack__init(ts, thread, crp, callstack, br_stack_sz))
                return NULL;
 
        return ts;
@@ -319,6 +342,9 @@ static int __thread_stack__flush(struct thread *thread, struct thread_stack *ts)
 
        if (!crp) {
                ts->cnt = 0;
+               ts->br_stack_pos = 0;
+               if (ts->br_stack_rb)
+                       ts->br_stack_rb->nr = 0;
                return 0;
        }
 
@@ -353,8 +379,33 @@ int thread_stack__flush(struct thread *thread)
        return err;
 }
 
+static void thread_stack__update_br_stack(struct thread_stack *ts, u32 flags,
+                                         u64 from_ip, u64 to_ip)
+{
+       struct branch_stack *bs = ts->br_stack_rb;
+       struct branch_entry *be;
+
+       if (!ts->br_stack_pos)
+               ts->br_stack_pos = ts->br_stack_sz;
+
+       ts->br_stack_pos -= 1;
+
+       be              = &bs->entries[ts->br_stack_pos];
+       be->from        = from_ip;
+       be->to          = to_ip;
+       be->flags.value = 0;
+       be->flags.abort = !!(flags & PERF_IP_FLAG_TX_ABORT);
+       be->flags.in_tx = !!(flags & PERF_IP_FLAG_IN_TX);
+       /* No support for mispredict */
+       be->flags.mispred = ts->mispred_all;
+
+       if (bs->nr < ts->br_stack_sz)
+               bs->nr += 1;
+}
+
 int thread_stack__event(struct thread *thread, int cpu, u32 flags, u64 from_ip,
-                       u64 to_ip, u16 insn_len, u64 trace_nr)
+                       u64 to_ip, u16 insn_len, u64 trace_nr, bool callstack,
+                       unsigned int br_stack_sz, bool mispred_all)
 {
        struct thread_stack *ts = thread__stack(thread, cpu);
 
@@ -362,12 +413,13 @@ int thread_stack__event(struct thread *thread, int cpu, u32 flags, u64 from_ip,
                return -EINVAL;
 
        if (!ts) {
-               ts = thread_stack__new(thread, cpu, NULL);
+               ts = thread_stack__new(thread, cpu, NULL, callstack, br_stack_sz);
                if (!ts) {
                        pr_warning("Out of memory: no thread stack\n");
                        return -ENOMEM;
                }
                ts->trace_nr = trace_nr;
+               ts->mispred_all = mispred_all;
        }
 
        /*
@@ -381,8 +433,14 @@ int thread_stack__event(struct thread *thread, int cpu, u32 flags, u64 from_ip,
                ts->trace_nr = trace_nr;
        }
 
-       /* Stop here if thread_stack__process() is in use */
-       if (ts->crp)
+       if (br_stack_sz)
+               thread_stack__update_br_stack(ts, flags, from_ip, to_ip);
+
+       /*
+        * Stop here if thread_stack__process() is in use, or not recording call
+        * stack.
+        */
+       if (ts->crp || !callstack)
                return 0;
 
        if (flags & PERF_IP_FLAG_CALL) {
@@ -430,6 +488,7 @@ static void __thread_stack__free(struct thread *thread, struct thread_stack *ts)
 {
        __thread_stack__flush(thread, ts);
        zfree(&ts->stack);
+       zfree(&ts->br_stack_rb);
 }
 
 static void thread_stack__reset(struct thread *thread, struct thread_stack *ts)
@@ -497,6 +556,199 @@ void thread_stack__sample(struct thread *thread, int cpu,
        chain->nr = i;
 }
 
+/*
+ * Hardware sample records, created some time after the event occurred, need to
+ * have subsequent addresses removed from the call chain.
+ */
+void thread_stack__sample_late(struct thread *thread, int cpu,
+                              struct ip_callchain *chain, size_t sz,
+                              u64 sample_ip, u64 kernel_start)
+{
+       struct thread_stack *ts = thread__stack(thread, cpu);
+       u64 sample_context = callchain_context(sample_ip, kernel_start);
+       u64 last_context, context, ip;
+       size_t nr = 0, j;
+
+       if (sz < 2) {
+               chain->nr = 0;
+               return;
+       }
+
+       if (!ts)
+               goto out;
+
+       /*
+        * When tracing kernel space, kernel addresses occur at the top of the
+        * call chain after the event occurred but before tracing stopped.
+        * Skip them.
+        */
+       for (j = 1; j <= ts->cnt; j++) {
+               ip = ts->stack[ts->cnt - j].ret_addr;
+               context = callchain_context(ip, kernel_start);
+               if (context == PERF_CONTEXT_USER ||
+                   (context == sample_context && ip == sample_ip))
+                       break;
+       }
+
+       last_context = sample_ip; /* Use sample_ip as an invalid context */
+
+       for (; nr < sz && j <= ts->cnt; nr++, j++) {
+               ip = ts->stack[ts->cnt - j].ret_addr;
+               context = callchain_context(ip, kernel_start);
+               if (context != last_context) {
+                       if (nr >= sz - 1)
+                               break;
+                       chain->ips[nr++] = context;
+                       last_context = context;
+               }
+               chain->ips[nr] = ip;
+       }
+out:
+       if (nr) {
+               chain->nr = nr;
+       } else {
+               chain->ips[0] = sample_context;
+               chain->ips[1] = sample_ip;
+               chain->nr = 2;
+       }
+}
+
+void thread_stack__br_sample(struct thread *thread, int cpu,
+                            struct branch_stack *dst, unsigned int sz)
+{
+       struct thread_stack *ts = thread__stack(thread, cpu);
+       const size_t bsz = sizeof(struct branch_entry);
+       struct branch_stack *src;
+       struct branch_entry *be;
+       unsigned int nr;
+
+       dst->nr = 0;
+
+       if (!ts)
+               return;
+
+       src = ts->br_stack_rb;
+       if (!src->nr)
+               return;
+
+       dst->nr = min((unsigned int)src->nr, sz);
+
+       be = &dst->entries[0];
+       nr = min(ts->br_stack_sz - ts->br_stack_pos, (unsigned int)dst->nr);
+       memcpy(be, &src->entries[ts->br_stack_pos], bsz * nr);
+
+       if (src->nr >= ts->br_stack_sz) {
+               sz -= nr;
+               be = &dst->entries[nr];
+               nr = min(ts->br_stack_pos, sz);
+               memcpy(be, &src->entries[0], bsz * ts->br_stack_pos);
+       }
+}
+
+/* Start of user space branch entries */
+static bool us_start(struct branch_entry *be, u64 kernel_start, bool *start)
+{
+       if (!*start)
+               *start = be->to && be->to < kernel_start;
+
+       return *start;
+}
+
+/*
+ * Start of branch entries after the ip fell in between 2 branches, or user
+ * space branch entries.
+ */
+static bool ks_start(struct branch_entry *be, u64 sample_ip, u64 kernel_start,
+                    bool *start, struct branch_entry *nb)
+{
+       if (!*start) {
+               *start = (nb && sample_ip >= be->to && sample_ip <= nb->from) ||
+                        be->from < kernel_start ||
+                        (be->to && be->to < kernel_start);
+       }
+
+       return *start;
+}
+
+/*
+ * Hardware sample records, created some time after the event occurred, need to
+ * have subsequent addresses removed from the branch stack.
+ */
+void thread_stack__br_sample_late(struct thread *thread, int cpu,
+                                 struct branch_stack *dst, unsigned int sz,
+                                 u64 ip, u64 kernel_start)
+{
+       struct thread_stack *ts = thread__stack(thread, cpu);
+       struct branch_entry *d, *s, *spos, *ssz;
+       struct branch_stack *src;
+       unsigned int nr = 0;
+       bool start = false;
+
+       dst->nr = 0;
+
+       if (!ts)
+               return;
+
+       src = ts->br_stack_rb;
+       if (!src->nr)
+               return;
+
+       spos = &src->entries[ts->br_stack_pos];
+       ssz  = &src->entries[ts->br_stack_sz];
+
+       d = &dst->entries[0];
+       s = spos;
+
+       if (ip < kernel_start) {
+               /*
+                * User space sample: start copying branch entries when the
+                * branch is in user space.
+                */
+               for (s = spos; s < ssz && nr < sz; s++) {
+                       if (us_start(s, kernel_start, &start)) {
+                               *d++ = *s;
+                               nr += 1;
+                       }
+               }
+
+               if (src->nr >= ts->br_stack_sz) {
+                       for (s = &src->entries[0]; s < spos && nr < sz; s++) {
+                               if (us_start(s, kernel_start, &start)) {
+                                       *d++ = *s;
+                                       nr += 1;
+                               }
+                       }
+               }
+       } else {
+               struct branch_entry *nb = NULL;
+
+               /*
+                * Kernel space sample: start copying branch entries when the ip
+                * falls in between 2 branches (or the branch is in user space
+                * because then the start must have been missed).
+                */
+               for (s = spos; s < ssz && nr < sz; s++) {
+                       if (ks_start(s, ip, kernel_start, &start, nb)) {
+                               *d++ = *s;
+                               nr += 1;
+                       }
+                       nb = s;
+               }
+
+               if (src->nr >= ts->br_stack_sz) {
+                       for (s = &src->entries[0]; s < spos && nr < sz; s++) {
+                               if (ks_start(s, ip, kernel_start, &start, nb)) {
+                                       *d++ = *s;
+                                       nr += 1;
+                               }
+                               nb = s;
+                       }
+               }
+       }
+
+       dst->nr = nr;
+}
+
 struct call_return_processor *
 call_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data),
                           void *data)
@@ -864,7 +1116,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
        }
 
        if (!ts) {
-               ts = thread_stack__new(thread, sample->cpu, crp);
+               ts = thread_stack__new(thread, sample->cpu, crp, true, 0);
                if (!ts)
                        return -ENOMEM;
                ts->comm = comm;
index e1ec5a58f1b2cda18fce0aa1b6457b16db8caf39..3bc47a42af8ee343bcf98790af05bedd0d97e228 100644 (file)
@@ -81,10 +81,19 @@ struct call_return_processor {
 };
 
 int thread_stack__event(struct thread *thread, int cpu, u32 flags, u64 from_ip,
-                       u64 to_ip, u16 insn_len, u64 trace_nr);
+                       u64 to_ip, u16 insn_len, u64 trace_nr, bool callstack,
+                       unsigned int br_stack_sz, bool mispred_all);
 void thread_stack__set_trace_nr(struct thread *thread, int cpu, u64 trace_nr);
 void thread_stack__sample(struct thread *thread, int cpu, struct ip_callchain *chain,
                          size_t sz, u64 ip, u64 kernel_start);
+void thread_stack__sample_late(struct thread *thread, int cpu,
+                              struct ip_callchain *chain, size_t sz, u64 ip,
+                              u64 kernel_start);
+void thread_stack__br_sample(struct thread *thread, int cpu,
+                            struct branch_stack *dst, unsigned int sz);
+void thread_stack__br_sample_late(struct thread *thread, int cpu,
+                                 struct branch_stack *dst, unsigned int sz,
+                                 u64 sample_ip, u64 kernel_start);
 int thread_stack__flush(struct thread *thread);
 void thread_stack__free(struct thread *thread);
 size_t thread_stack__depth(struct thread *thread, int cpu);
index 28b719388028bce9f9384b9c95c9cff51193c800..665e5c0618ed3d330ff1bc0238f9bc1a3d4cdee7 100644 (file)
@@ -47,6 +47,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
                thread->tid = tid;
                thread->ppid = -1;
                thread->cpu = -1;
+               thread->lbr_stitch_enable = false;
                INIT_LIST_HEAD(&thread->namespaces_list);
                INIT_LIST_HEAD(&thread->comm_list);
                init_rwsem(&thread->namespaces_lock);
@@ -110,6 +111,7 @@ void thread__delete(struct thread *thread)
 
        exit_rwsem(&thread->namespaces_lock);
        exit_rwsem(&thread->comm_lock);
+       thread__free_stitch_list(thread);
        free(thread);
 }
 
@@ -452,3 +454,25 @@ int thread__memcpy(struct thread *thread, struct machine *machine,
 
        return dso__data_read_offset(al.map->dso, machine, offset, buf, len);
 }
+
+void thread__free_stitch_list(struct thread *thread)
+{
+       struct lbr_stitch *lbr_stitch = thread->lbr_stitch;
+       struct stitch_list *pos, *tmp;
+
+       if (!lbr_stitch)
+               return;
+
+       list_for_each_entry_safe(pos, tmp, &lbr_stitch->lists, node) {
+               list_del_init(&pos->node);
+               free(pos);
+       }
+
+       list_for_each_entry_safe(pos, tmp, &lbr_stitch->free_lists, node) {
+               list_del_init(&pos->node);
+               free(pos);
+       }
+
+       zfree(&lbr_stitch->prev_lbr_cursor);
+       zfree(&thread->lbr_stitch);
+}
index 20b96b5d1f1593117225ec93c376596377e38e17..b066fb30d203da8eb87ace26fb3998b4a1ee3fad 100644 (file)
@@ -13,6 +13,8 @@
 #include <strlist.h>
 #include <intlist.h>
 #include "rwsem.h"
+#include "event.h"
+#include "callchain.h"
 
 struct addr_location;
 struct map;
@@ -20,6 +22,13 @@ struct perf_record_namespaces;
 struct thread_stack;
 struct unwind_libunwind_ops;
 
+struct lbr_stitch {
+       struct list_head                lists;
+       struct list_head                free_lists;
+       struct perf_sample              prev_sample;
+       struct callchain_cursor_node    *prev_lbr_cursor;
+};
+
 struct thread {
        union {
                struct rb_node   rb_node;
@@ -46,6 +55,10 @@ struct thread {
        struct srccode_state    srccode_state;
        bool                    filter;
        int                     filter_entry_depth;
+
+       /* LBR call stack stitch */
+       bool                    lbr_stitch_enable;
+       struct lbr_stitch       *lbr_stitch;
 };
 
 struct machine;
@@ -142,4 +155,6 @@ static inline bool thread__is_filtered(struct thread *thread)
        return false;
 }
 
+void thread__free_stitch_list(struct thread *thread);
+
 #endif /* __PERF_THREAD_H */
index 3dce2de9d005daafd0ceecbacacbb40ce7178676..27945eeb0cb589cb022dbedcc6630b4ec4cb227f 100644 (file)
@@ -77,7 +77,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
                                opts->freq ? "Hz" : "");
        }
 
-       ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel));
+       ret += SNPRINTF(bf + ret, size - ret, "%s", evsel__name(top->sym_evsel));
 
        ret += SNPRINTF(bf + ret, size - ret, "], ");
 
index f117d4f4821e0a26d441aec796c0ae6dcefcade4..ff8391208ecd63b9fd770e7f262c43e4306a05c6 100644 (file)
@@ -18,7 +18,7 @@ struct perf_session;
 
 struct perf_top {
        struct perf_tool   tool;
-       struct evlist *evlist;
+       struct evlist *evlist, *sb_evlist;
        struct record_opts record_opts;
        struct annotation_options annotation_opts;
        struct evswitch    evswitch;
@@ -36,6 +36,7 @@ struct perf_top {
        bool               use_tui, use_stdio;
        bool               vmlinux_warned;
        bool               dump_symtab;
+       bool               stitch_lbr;
        struct hist_entry  *sym_filter_entry;
        struct evsel       *sym_evsel;
        struct perf_session *session;
index 8593d3c200c610e7c0d8977058fdb3d63b536fa6..f507dff713c9f8e55df4a233517ce531132a239f 100644 (file)
@@ -75,7 +75,7 @@ static void skip(int size)
                r = size > BUFSIZ ? BUFSIZ : size;
                do_read(buf, r);
                size -= r;
-       };
+       }
 }
 
 static unsigned int read4(struct tep_handle *pevent)
index d707c9624dd973534924ced6103f4cdcdaa6cc0f..37a9492edb3ebf094e348c1ab4e72e9dfe9d2378 100644 (file)
@@ -290,6 +290,7 @@ int perf_event_paranoid(void)
 bool perf_event_paranoid_check(int max_level)
 {
        return perf_cap__capable(CAP_SYS_ADMIN) ||
+                       perf_cap__capable(CAP_PERFMON) ||
                        perf_event_paranoid() <= max_level;
 }
 
index d3755ea70d4da1bba19990706d32a5919f928447..0ba61a2c4d81c83247e7d92a2fc36e0e0c1183e7 100644 (file)
@@ -62,7 +62,7 @@ int cmd_info(int argc, char **argv)
                default:
                        print_wrong_arg_exit();
                }
-       };
+       }
 
        if (!params.params)
                params.params = 0x7;
index 3cca6f715dd93a506aee3248bdd2d97deba6f11b..052044d7e012098b531fc2bc439ffb2253ef903b 100644 (file)
@@ -72,7 +72,7 @@ int cmd_set(int argc, char **argv)
                default:
                        print_wrong_arg_exit();
                }
-       };
+       }
 
        if (!params.params)
                print_wrong_arg_exit();
index 20f46348271b1b9dcdadfff8918150a5944de4d8..5edd35bd9ee924e906a9b45ffda2998848df339a 100644 (file)
@@ -117,7 +117,7 @@ static int amd_fam14h_get_pci_info(struct cstate *state,
                break;
        default:
                return -1;
-       };
+       }
        return 0;
 }
 
index a65f7d011513a3f3d311790cf6b9c81aee99fb1d..8b42c2f0a5b0f485d7cb249830e4cf9a913d19e0 100644 (file)
@@ -53,7 +53,7 @@ static int cpuidle_start(void)
                        dprint("CPU %d - State: %d - Val: %llu\n",
                               cpu, state, previous_count[cpu][state]);
                }
-       };
+       }
        return 0;
 }
 
@@ -72,7 +72,7 @@ static int cpuidle_stop(void)
                        dprint("CPU %d - State: %d - Val: %llu\n",
                               cpu, state, previous_count[cpu][state]);
                }
-       };
+       }
        return 0;
 }
 
@@ -172,7 +172,7 @@ static struct cpuidle_monitor *cpuidle_register(void)
                cpuidle_cstates[num].id = num;
                cpuidle_cstates[num].get_count_percent =
                        cpuidle_get_count_percent;
-       };
+       }
 
        /* Free this at program termination */
        previous_count = malloc(sizeof(long long *) * cpu_count);
index 97ad3233a521e55b8653379e9666d2cb1165b8ad..55e55b6b42f9edd3a16498ce9bd6afc9fba77215 100644 (file)
@@ -79,7 +79,7 @@ static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
                break;
        default:
                return -1;
-       };
+       }
        if (read_msr(cpu, msr, val))
                return -1;
        return 0;
index 114271165182954959226001286c57da78496c9c..16eaf006f61f28b3dbd519ab923e5f0a059d385b 100644 (file)
@@ -91,7 +91,7 @@ static int nhm_get_count(enum intel_nhm_id id, unsigned long long *val,
                break;
        default:
                return -1;
-       };
+       }
        if (read_msr(cpu, msr, val))
                return -1;
 
index df8b223cc09607301bf3a81cccaeeae457e96c7f..811d63ab17a769ef053cb288f8510d8ea161692d 100644 (file)
@@ -77,7 +77,7 @@ static int snb_get_count(enum intel_snb_id id, unsigned long long *val,
                break;
        default:
                return -1;
-       };
+       }
        if (read_msr(cpu, msr, val))
                return -1;
        return 0;
index 4f80ad7d72755b308bdc4d963f620a9548dcdbb3..962e5768681c6bf7878842aa8a2b66582b8c745b 100644 (file)
@@ -125,7 +125,7 @@ acpi_suspend_begin:
 suspend_console:
 acpi_pm_prepare:
 syscore_suspend:
-arch_enable_nonboot_cpus_end:
+arch_thaw_secondary_cpus_end:
 syscore_resume:
 acpi_pm_finish:
 resume_console:
index 9b0404d107686aa5c63a5f9a06dbe76d00b47514..602e64b68ba76405f56650fa3b354fcfdb45874e 100755 (executable)
@@ -198,7 +198,7 @@ class SystemValues:
                'suspend_console': {},
                'acpi_pm_prepare': {},
                'syscore_suspend': {},
-               'arch_enable_nonboot_cpus_end': {},
+               'arch_thaw_secondary_cpus_end': {},
                'syscore_resume': {},
                'acpi_pm_finish': {},
                'resume_console': {},
index b73763489410e0ac671788dc4982aecac62fde37..9f68f51ca6523f9aa19569587e3ab4523296b93c 100644 (file)
@@ -15,7 +15,7 @@ struct process_cmd_struct {
        int arg;
 };
 
-static const char *version_str = "v1.3";
+static const char *version_str = "v1.4";
 static const int supported_api_ver = 1;
 static struct isst_if_platform_info isst_platform_info;
 static char *progname;
@@ -25,7 +25,7 @@ static FILE *outf;
 static int cpu_model;
 static int cpu_stepping;
 
-#define MAX_CPUS_IN_ONE_REQ 64
+#define MAX_CPUS_IN_ONE_REQ 256
 static short max_target_cpus;
 static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
 
@@ -653,7 +653,7 @@ void set_cpu_mask_from_punit_coremask(int cpu, unsigned long long core_mask,
        pkg_id = get_physical_package_id(cpu);
 
        for (i = 0; i < 64; ++i) {
-               if (core_mask & BIT(i)) {
+               if (core_mask & BIT_ULL(i)) {
                        int j;
 
                        for (j = 0; j < topo_max_cpus; ++j) {
@@ -1169,6 +1169,7 @@ static void dump_clx_n_config_for_cpu(int cpu, void *arg1, void *arg2,
 
                ctdp_level = &clx_n_pkg_dev.ctdp_level[0];
                pbf_info = &ctdp_level->pbf_info;
+               clx_n_pkg_dev.processed = 1;
                isst_ctdp_display_information(cpu, outf, tdp_level, &clx_n_pkg_dev);
                free_cpu_set(ctdp_level->core_cpumask);
                free_cpu_set(pbf_info->core_cpumask);
@@ -1631,6 +1632,8 @@ static int set_pbf_core_power(int cpu)
 static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                            void *arg4)
 {
+       struct isst_pkg_ctdp_level_info ctdp_level;
+       struct isst_pkg_ctdp pkg_dev;
        int ret;
        int status = *(int *)arg4;
 
@@ -1646,6 +1649,24 @@ static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                goto disp_result;
        }
 
+       ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+       if (ret) {
+               isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
+               goto disp_result;
+       }
+
+       ret = isst_get_ctdp_control(cpu, pkg_dev.current_level, &ctdp_level);
+       if (ret) {
+               isst_display_error_info_message(1, "Failed to get current level", 0, 0);
+               goto disp_result;
+       }
+
+       if (!ctdp_level.pbf_support) {
+               isst_display_error_info_message(1, "base-freq feature is not present at this level", 1, pkg_dev.current_level);
+               ret = -1;
+               goto disp_result;
+       }
+
        if (auto_mode && status) {
                ret = set_pbf_core_power(cpu);
                if (ret)
@@ -1772,10 +1793,30 @@ static void dump_fact_config(int arg)
 static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                             void *arg4)
 {
+       struct isst_pkg_ctdp_level_info ctdp_level;
+       struct isst_pkg_ctdp pkg_dev;
        int ret;
        int status = *(int *)arg4;
 
-       if (auto_mode && status) {
+       ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+       if (ret) {
+               isst_display_error_info_message(1, "Failed to get number of levels", 0, 0);
+               goto disp_results;
+       }
+
+       ret = isst_get_ctdp_control(cpu, pkg_dev.current_level, &ctdp_level);
+       if (ret) {
+               isst_display_error_info_message(1, "Failed to get current level", 0, 0);
+               goto disp_results;
+       }
+
+       if (!ctdp_level.fact_support) {
+               isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, pkg_dev.current_level);
+               ret = -1;
+               goto disp_results;
+       }
+
+       if (status) {
                ret = isst_pm_qos_config(cpu, 1, 1);
                if (ret)
                        goto disp_results;
index 67c9b113963177c06d2cb6f7a6a25c9817e45da3..a7f4337c57776566f1aca8bbb8808e0ab68f17ee 100644 (file)
@@ -912,16 +912,16 @@ int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
                        return ret;
 
                if (ctdp_level.fact_enabled) {
-                       debug_printf("Turbo-freq feature must be disabled first\n");
+                       isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0);
                        return -EINVAL;
                }
                ret = isst_write_pm_config(cpu, 0);
                if (ret)
-                       isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error\n", 0, 0);
+                       isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0);
        } else {
                ret = isst_write_pm_config(cpu, 1);
                if (ret)
-                       isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error\n", 0, 0);
+                       isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0);
        }
 
        ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
index 51dbaa5f02ec5b733f9080dde42ed733cbaaa87c..e105fece47b617cbcf50e6006aac0246a0b79ee9 100644 (file)
@@ -316,21 +316,31 @@ void isst_ctdp_display_core_info(int cpu, FILE *outf, char *prefix,
 {
        char header[256];
        char value[256];
+       int level = 1;
+
+       if (out_format_is_json()) {
+               snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d",
+                        get_physical_package_id(cpu), get_physical_die_id(cpu),
+                        cpu);
+               format_and_print(outf, level++, header, NULL);
+       } else {
+               snprintf(header, sizeof(header), "package-%d",
+                        get_physical_package_id(cpu));
+               format_and_print(outf, level++, header, NULL);
+               snprintf(header, sizeof(header), "die-%d",
+                        get_physical_die_id(cpu));
+               format_and_print(outf, level++, header, NULL);
+               snprintf(header, sizeof(header), "cpu-%d", cpu);
+               format_and_print(outf, level++, header, NULL);
+       }
 
-       snprintf(header, sizeof(header), "package-%d",
-                get_physical_package_id(cpu));
-       format_and_print(outf, 1, header, NULL);
-       snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
-       format_and_print(outf, 2, header, NULL);
-       snprintf(header, sizeof(header), "cpu-%d", cpu);
-       format_and_print(outf, 3, header, NULL);
        if (str0 && !val)
                snprintf(value, sizeof(value), "%s", str0);
        else if (str1 && val)
                snprintf(value, sizeof(value), "%s", str1);
        else
                snprintf(value, sizeof(value), "%u", val);
-       format_and_print(outf, 4, prefix, value);
+       format_and_print(outf, level, prefix, value);
 
        format_and_print(outf, 1, NULL, NULL);
 }
@@ -470,7 +480,7 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
                                _isst_pbf_display_information(cpu, outf,
                                                              tdp_level,
                                                          &ctdp_level->pbf_info,
-                                                             level + 1);
+                                                             level + 2);
                        continue;
                }
 
index 2e1afd856a78b64c396feef6cb5fceb21f4e78dd..094ba4589a9c095438d6e9c3a3fa0338784d1173 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/ioctl.h>
 
 #define BIT(x) (1 << (x))
+#define BIT_ULL(nr) (1ULL << (nr))
 #define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
 #define GENMASK_ULL(h, l)                                                      \
        (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
index 2249a1546cc118feed4036d56a9c306337f26984..ada881afb489a595c536401378ff58342bcc4bcf 100644 (file)
@@ -52,7 +52,9 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN)
 clean:
        rm -f $(ALL_PROGRAMS)
        rm -rf $(OUTPUT)include/
-       find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
+       find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete
+       find $(if $(OUTPUT),$(OUTPUT),.) -name '\.*.o.d' -delete
+       find $(if $(OUTPUT),$(OUTPUT),.) -name '\.*.o.cmd' -delete
 
 install: $(ALL_PROGRAMS)
        install -d -m 755 $(DESTDIR)$(bindir);          \
index 27967dd90f8f3f772f7c9a83d2fd8c823fc86f69..eec23fa693bd59128794b464316315fcebb2f16d 100644 (file)
@@ -128,18 +128,22 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
                .bits_per_word = bits,
        };
 
-       if (mode & SPI_TX_QUAD)
+       if (mode & SPI_TX_OCTAL)
+               tr.tx_nbits = 8;
+       else if (mode & SPI_TX_QUAD)
                tr.tx_nbits = 4;
        else if (mode & SPI_TX_DUAL)
                tr.tx_nbits = 2;
-       if (mode & SPI_RX_QUAD)
+       if (mode & SPI_RX_OCTAL)
+               tr.rx_nbits = 8;
+       else if (mode & SPI_RX_QUAD)
                tr.rx_nbits = 4;
        else if (mode & SPI_RX_DUAL)
                tr.rx_nbits = 2;
        if (!(mode & SPI_LOOP)) {
-               if (mode & (SPI_TX_QUAD | SPI_TX_DUAL))
+               if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
                        tr.rx_buf = 0;
-               else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL))
+               else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
                        tr.tx_buf = 0;
        }
 
@@ -187,6 +191,7 @@ static void print_usage(const char *prog)
             "  -R --ready    slave pulls low to pause\n"
             "  -2 --dual     dual transfer\n"
             "  -4 --quad     quad transfer\n"
+            "  -8 --octal    octal transfer\n"
             "  -S --size     transfer size\n"
             "  -I --iter     iterations\n");
        exit(1);
@@ -213,13 +218,14 @@ static void parse_opts(int argc, char *argv[])
                        { "dual",    0, 0, '2' },
                        { "verbose", 0, 0, 'v' },
                        { "quad",    0, 0, '4' },
+                       { "octal",   0, 0, '8' },
                        { "size",    1, 0, 'S' },
                        { "iter",    1, 0, 'I' },
                        { NULL, 0, 0, 0 },
                };
                int c;
 
-               c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:vS:I:",
+               c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
                                lopts, NULL);
 
                if (c == -1)
@@ -280,6 +286,9 @@ static void parse_opts(int argc, char *argv[])
                case '4':
                        mode |= SPI_TX_QUAD;
                        break;
+               case '8':
+                       mode |= SPI_TX_OCTAL;
+                       break;
                case 'S':
                        transfer_size = atoi(optarg);
                        break;
@@ -295,6 +304,8 @@ static void parse_opts(int argc, char *argv[])
                        mode |= SPI_RX_DUAL;
                if (mode & SPI_TX_QUAD)
                        mode |= SPI_RX_QUAD;
+               if (mode & SPI_TX_OCTAL)
+                       mode |= SPI_RX_OCTAL;
        }
 }
 
index a253a064e6e059056d6389b87c5540e5cc37ebfe..58f4aa593b1b527f49f3b042c16920cf4e29a082 100644 (file)
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
        BPF_LD_MAP_FD(BPF_REG_1, 0),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9),
+       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8),
        /* r1 = [0x00, 0xff] */
        BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 0xffffff80 >> 1),
         *      [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]
         */
        BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 0xffffff80 >> 1),
-       /* r1 = 0 or
-        *      [0x00ff'ffff'ff00'0000, 0x00ff'ffff'ffff'ffff]
-        */
-       BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 8),
        /* error on OOB pointer computation */
        BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
        /* exit */
        },
        .fixup_map_hash_8b = { 3 },
        /* not actually fully unbounded, but the bound is very high */
-       .errstr = "value 72057594021150720 makes map_value pointer be out of bounds",
-       .result = REJECT
+       .errstr_unpriv = "R1 has unknown scalar with mixed signed bounds, pointer arithmetic with it prohibited for !root",
+       .result_unpriv = REJECT,
+       .errstr = "value -4294967168 makes map_value pointer be out of bounds",
+       .result = REJECT,
 },
 {
        "bounds check after truncation of boundary-crossing range (2)",
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
        BPF_LD_MAP_FD(BPF_REG_1, 0),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9),
+       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8),
        /* r1 = [0x00, 0xff] */
        BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 0xffffff80 >> 1),
         *      [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]
         */
        BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 0xffffff80 >> 1),
-       /* r1 = 0 or
-        *      [0x00ff'ffff'ff00'0000, 0x00ff'ffff'ffff'ffff]
-        */
-       BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 8),
        /* error on OOB pointer computation */
        BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
        /* exit */
        },
        .fixup_map_hash_8b = { 3 },
        /* not actually fully unbounded, but the bound is very high */
-       .errstr = "value 72057594021150720 makes map_value pointer be out of bounds",
-       .result = REJECT
+       .errstr_unpriv = "R1 has unknown scalar with mixed signed bounds, pointer arithmetic with it prohibited for !root",
+       .result_unpriv = REJECT,
+       .errstr = "value -4294967168 makes map_value pointer be out of bounds",
+       .result = REJECT,
 },
 {
        "bounds check after wrapping 32-bit addition",
        },
        .result = ACCEPT
 },
+{
+       "assigning 32bit bounds to 64bit for wA = 0, wB = wA",
+       .insns = {
+       BPF_LDX_MEM(BPF_W, BPF_REG_8, BPF_REG_1,
+                   offsetof(struct __sk_buff, data_end)),
+       BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
+                   offsetof(struct __sk_buff, data)),
+       BPF_MOV32_IMM(BPF_REG_9, 0),
+       BPF_MOV32_REG(BPF_REG_2, BPF_REG_9),
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_7),
+       BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_2),
+       BPF_MOV64_REG(BPF_REG_3, BPF_REG_6),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 8),
+       BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_8, 1),
+       BPF_LDX_MEM(BPF_W, BPF_REG_5, BPF_REG_6, 0),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+       .result = ACCEPT,
+       .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
+},
index 1cef54458aff916b2f00af33deef703cb3101ab0..2aa9a3852a84209bcab4fb8b45a5800dbed3e5be 100755 (executable)
@@ -10,7 +10,7 @@
 . ./common_tests
 
 prlog -n "Checking pstore console is registered ... "
-dmesg | grep -q "console \[pstore"
+dmesg | grep -Eq "console \[(pstore|${backend})"
 show_result $?
 
 prlog -n "Checking /dev/pmsg0 exists ... "
diff --git a/tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh b/tools/testing/selftests/rcutorture/bin/kcsan-collapse.sh
new file mode 100755 (executable)
index 0000000..e5cc6b2
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# If this was a KCSAN run, collapse the reports in the various console.log
+# files onto pairs of functions.
+#
+# Usage: kcsan-collapse.sh resultsdir
+#
+# Copyright (C) 2020 Facebook, Inc.
+#
+# Authors: Paul E. McKenney <paulmck@kernel.org>
+
+if test -z "$TORTURE_KCONFIG_KCSAN_ARG"
+then
+       exit 0
+fi
+cat $1/*/console.log |
+       grep "BUG: KCSAN: " |
+       sed -e 's/^\[[^]]*] //' |
+       sort |
+       uniq -c |
+       sort -k1nr > $1/kcsan.sum
index 9d9a41625dd90061046d3bd272fcf226bf59c0b1..1706cd4466b429d0cae27d65a957bfb0e3d23eba 100755 (executable)
@@ -41,7 +41,21 @@ else
                title="$title ($ngpsps/s)"
        fi
        echo $title $stopstate $fwdprog
-       nclosecalls=`grep --binary-files=text 'torture: Reader Batch' $i/console.log | tail -1 | awk '{for (i=NF-8;i<=NF;i++) sum+=$i; } END {print sum}'`
+       nclosecalls=`grep --binary-files=text 'torture: Reader Batch' $i/console.log | tail -1 | \
+               awk -v sum=0 '
+               {
+                       for (i = 0; i <= NF; i++) {
+                               sum += $i;
+                               if ($i ~ /Batch:/) {
+                                       sum = 0;
+                                       i = i + 2;
+                               }
+                       }
+               }
+
+               END {
+                       print sum
+               }'`
        if test -z "$nclosecalls"
        then
                exit 0
index 0326f4a5ff9c35f547adb7497a5ec97a6a3bc840..736f04749b90783db481504f867ca0fc5614ffaf 100755 (executable)
@@ -70,6 +70,15 @@ do
                        fi
                fi
        done
+       if test -f "$rd/kcsan.sum"
+       then
+               if test -s "$rd/kcsan.sum"
+               then
+                       echo KCSAN summary in $rd/kcsan.sum
+               else
+                       echo Clean KCSAN run in $rd
+               fi
+       fi
 done
 EDITOR=echo kvm-find-errors.sh "${@: -1}" > $T 2>&1
 ret=$?
index e0352304b98b4c7f045e67560e8c65dfb2a4a6a3..6ff611c630d1efdf1d73c3bd0037d29bef8ab76a 100755 (executable)
@@ -44,30 +44,32 @@ then
 fi
 echo ' ---' `date`: Starting build
 echo ' ---' Kconfig fragment at: $config_template >> $resdir/log
-touch $resdir/ConfigFragment.input $resdir/ConfigFragment
-if test -r "$config_dir/CFcommon"
-then
-       echo " --- $config_dir/CFcommon" >> $resdir/ConfigFragment.input
-       cat < $config_dir/CFcommon >> $resdir/ConfigFragment.input
-       config_override.sh $config_dir/CFcommon $config_template > $T/Kc1
-       grep '#CHECK#' $config_dir/CFcommon >> $resdir/ConfigFragment
-else
-       cp $config_template $T/Kc1
-fi
-echo " --- $config_template" >> $resdir/ConfigFragment.input
-cat $config_template >> $resdir/ConfigFragment.input
-grep '#CHECK#' $config_template >> $resdir/ConfigFragment
-if test -n "$TORTURE_KCONFIG_ARG"
-then
-       echo $TORTURE_KCONFIG_ARG | tr -s " " "\012" > $T/cmdline
-       echo " --- --kconfig argument" >> $resdir/ConfigFragment.input
-       cat $T/cmdline >> $resdir/ConfigFragment.input
-       config_override.sh $T/Kc1 $T/cmdline > $T/Kc2
-       # Note that "#CHECK#" is not permitted on commandline.
-else
-       cp $T/Kc1 $T/Kc2
-fi
-cat $T/Kc2 >> $resdir/ConfigFragment
+touch $resdir/ConfigFragment.input
+
+# Combine additional Kconfig options into an existing set such that
+# newer options win.  The first argument is the Kconfig source ID, the
+# second the to-be-updated file within $T, and the third and final the
+# list of additional Kconfig options.  Note that a $2.tmp file is
+# created when doing the update.
+config_override_param () {
+       if test -n "$3"
+       then
+               echo $3 | sed -e 's/^ *//' -e 's/ *$//' | tr -s " " "\012" > $T/Kconfig_args
+               echo " --- $1" >> $resdir/ConfigFragment.input
+               cat $T/Kconfig_args >> $resdir/ConfigFragment.input
+               config_override.sh $T/$2 $T/Kconfig_args > $T/$2.tmp
+               mv $T/$2.tmp $T/$2
+               # Note that "#CHECK#" is not permitted on commandline.
+       fi
+}
+
+echo > $T/KcList
+config_override_param "$config_dir/CFcommon" KcList "`cat $config_dir/CFcommon 2> /dev/null`"
+config_override_param "$config_template" KcList "`cat $config_template 2> /dev/null`"
+config_override_param "--kasan options" KcList "$TORTURE_KCONFIG_KASAN_ARG"
+config_override_param "--kcsan options" KcList "$TORTURE_KCONFIG_KCSAN_ARG"
+config_override_param "--kconfig argument" KcList "$TORTURE_KCONFIG_ARG"
+cp $T/KcList $resdir/ConfigFragment
 
 base_resdir=`echo $resdir | sed -e 's/\.[0-9]\+$//'`
 if test "$base_resdir" != "$resdir" -a -f $base_resdir/bzImage -a -f $base_resdir/vmlinux
@@ -80,7 +82,7 @@ then
        ln -s $base_resdir/.config $resdir  # for kvm-recheck.sh
        # Arch-independent indicator
        touch $resdir/builtkernel
-elif kvm-build.sh $T/Kc2 $resdir
+elif kvm-build.sh $T/KcList $resdir
 then
        # Had to build a kernel for this test.
        QEMU="`identify_qemu vmlinux`"
index 2315e2ec12d69b6a73afd070777e937f307ca5ed..c279cf9cb0102fe05f4e5e2679300224a3312e5a 100755 (executable)
@@ -31,6 +31,8 @@ TORTURE_DEFCONFIG=defconfig
 TORTURE_BOOT_IMAGE=""
 TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
 TORTURE_KCONFIG_ARG=""
+TORTURE_KCONFIG_KASAN_ARG=""
+TORTURE_KCONFIG_KCSAN_ARG=""
 TORTURE_KMAKE_ARG=""
 TORTURE_QEMU_MEM=512
 TORTURE_SHUTDOWN_GRACE=180
@@ -133,6 +135,12 @@ do
                TORTURE_KCONFIG_ARG="$2"
                shift
                ;;
+       --kasan)
+               TORTURE_KCONFIG_KASAN_ARG="CONFIG_DEBUG_INFO=y CONFIG_KASAN=y"; export TORTURE_KCONFIG_KASAN_ARG
+               ;;
+       --kcsan)
+               TORTURE_KCONFIG_KCSAN_ARG="CONFIG_DEBUG_INFO=y CONFIG_KCSAN=y CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n CONFIG_KCSAN_REPORT_ONCE_IN_MS=100000 CONFIG_KCSAN_VERBOSE=y CONFIG_KCSAN_INTERRUPT_WATCHER=y"; export TORTURE_KCONFIG_KCSAN_ARG
+               ;;
        --kmake-arg)
                checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$'
                TORTURE_KMAKE_ARG="$2"
@@ -310,6 +318,8 @@ TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
 TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG
 TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD
 TORTURE_KCONFIG_ARG="$TORTURE_KCONFIG_ARG"; export TORTURE_KCONFIG_ARG
+TORTURE_KCONFIG_KASAN_ARG="$TORTURE_KCONFIG_KASAN_ARG"; export TORTURE_KCONFIG_KASAN_ARG
+TORTURE_KCONFIG_KCSAN_ARG="$TORTURE_KCONFIG_KCSAN_ARG"; export TORTURE_KCONFIG_KCSAN_ARG
 TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG
 TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD
 TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE
@@ -464,6 +474,7 @@ echo
 echo
 echo " --- `date` Test summary:"
 echo Results directory: $resdir/$ds
+kcsan-collapse.sh $resdir/$ds
 kvm-recheck.sh $resdir/$ds
 ___EOF___
 
index c3c1fb5a9e1f5d5a72d4d25f3c509cea31414efb..f2b20db9e296b3174cdb63c20c2fa03c65b680a0 100644 (file)
@@ -14,3 +14,6 @@ TINY02
 TASKS01
 TASKS02
 TASKS03
+RUDE01
+TRACE01
+TRACE02
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/RUDE01 b/tools/testing/selftests/rcutorture/configs/rcu/RUDE01
new file mode 100644 (file)
index 0000000..bafe94c
--- /dev/null
@@ -0,0 +1,10 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+#CHECK#CONFIG_PROVE_RCU=y
+CONFIG_RCU_EXPERT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/RUDE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/RUDE01.boot
new file mode 100644 (file)
index 0000000..9363708
--- /dev/null
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks-rude
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRACE01 b/tools/testing/selftests/rcutorture/configs/rcu/TRACE01
new file mode 100644 (file)
index 0000000..12e7661
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=y
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=n
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+#CHECK#CONFIG_PROVE_RCU=y
+CONFIG_TASKS_TRACE_RCU_READ_MB=y
+CONFIG_RCU_EXPERT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRACE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TRACE01.boot
new file mode 100644 (file)
index 0000000..9675ad6
--- /dev/null
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks-tracing
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRACE02 b/tools/testing/selftests/rcutorture/configs/rcu/TRACE02
new file mode 100644 (file)
index 0000000..b69ed66
--- /dev/null
@@ -0,0 +1,11 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
+CONFIG_HOTPLUG_CPU=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+CONFIG_DEBUG_LOCK_ALLOC=n
+CONFIG_PROVE_LOCKING=n
+#CHECK#CONFIG_PROVE_RCU=n
+CONFIG_TASKS_TRACE_RCU_READ_MB=n
+CONFIG_RCU_EXPERT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRACE02.boot b/tools/testing/selftests/rcutorture/configs/rcu/TRACE02.boot
new file mode 100644 (file)
index 0000000..9675ad6
--- /dev/null
@@ -0,0 +1 @@
+rcutorture.torture_type=tasks-tracing
index 2debe7891aeb5e75f76fea4b6ac6c4dfa18ec469..7311f84a587658c2d456f51fd96fb63c3f6af196 100644 (file)
@@ -1,5 +1,5 @@
 CONFIG_SMP=y
-CONFIG_NR_CPUS=100
+CONFIG_NR_CPUS=56
 CONFIG_PREEMPT_NONE=y
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=n
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json
new file mode 100644 (file)
index 0000000..1cda2e1
--- /dev/null
@@ -0,0 +1,21 @@
+[
+    {
+        "id": "83be",
+        "name": "Create FQ-PIE with invalid number of flows",
+        "category": [
+            "qdisc",
+            "fq_pie"
+        ],
+        "setup": [
+            "$IP link add dev $DUMMY type dummy || /bin/true"
+        ],
+        "cmdUnderTest": "$TC qdisc add dev $DUMMY root fq_pie flows 65536",
+        "expExitCode": "2",
+        "verifyCmd": "$TC qdisc show dev $DUMMY",
+        "matchPattern": "qdisc",
+        "matchCount": "0",
+        "teardown": [
+            "$IP link del dev $DUMMY"
+        ]
+    }
+]
index 9803dbb54181ce8cac6111d83b8c36c58331d959..b50c2085c1ac0f1e50abc219a2bdab60b2b790bf 100644 (file)
@@ -57,7 +57,6 @@ CONFIG_RCU_EQS_DEBUG=y
 CONFIG_USER_STACKTRACE_SUPPORT=y
 CONFIG_DEBUG_SG=y
 CONFIG_DEBUG_NOTIFIERS=y
-CONFIG_DOUBLEFAULT=y
 CONFIG_X86_DEBUG_FPU=y
 CONFIG_DEBUG_SECTION_MISMATCH=y
 CONFIG_DEBUG_PAGEALLOC=y
index 48d0ec44ad77e52245a0ab571c726c39aa8af0ab..53b3ba9173ba73bbe9430070064eb8e13e802e66 100644 (file)
@@ -1387,9 +1387,7 @@ static inline void hyp_cpu_pm_exit(void)
 
 static int init_common_resources(void)
 {
-       kvm_set_ipa_limit();
-
-       return 0;
+       return kvm_set_ipa_limit();
 }
 
 static int init_subsystems(void)
index 89a14ec8b33bb2009786d921759868943a152e24..d2339a2b9fb9c5a6ebdc9f6c48d399f8c49423bb 100644 (file)
@@ -302,7 +302,7 @@ static unsigned long vgic_v3_uaccess_read_pending(struct kvm_vcpu *vcpu,
         * pending state of interrupt is latched in pending_latch variable.
         * Userspace will save and restore pending state and line_level
         * separately.
-        * Refer to Documentation/virt/kvm/devices/arm-vgic-v3.txt
+        * Refer to Documentation/virt/kvm/devices/arm-vgic-v3.rst
         * for handling of ISPENDR and ICPENDR.
         */
        for (i = 0; i < len * 8; i++) {
index 769e4802645ee8ef30f3761ba7c0ddc73bf652b2..64fcd75111108c6b7b3b5b74e434339138c44a57 100644 (file)
@@ -42,7 +42,7 @@
                            VGIC_AFFINITY_LEVEL(val, 3))
 
 /*
- * As per Documentation/virt/kvm/devices/arm-vgic-v3.txt,
+ * As per Documentation/virt/kvm/devices/arm-vgic-v3.rst,
  * below macros are defined for CPUREG encoding.
  */
 #define KVM_REG_ARM_VGIC_SYSREG_OP0_MASK   0x000000000000c000
@@ -63,7 +63,7 @@
                                      KVM_REG_ARM_VGIC_SYSREG_OP2_MASK)
 
 /*
- * As per Documentation/virt/kvm/devices/arm-vgic-its.txt,
+ * As per Documentation/virt/kvm/devices/arm-vgic-its.rst,
  * below macros are defined for ITS table entry encoding.
  */
 #define KVM_ITS_CTE_VALID_SHIFT                63